Security+Jwt登录

Security工作原理

1.依赖

点击查看代码
 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.75</version>
        </dependency>

        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.2.2</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!--knife4j-->
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>2.0.9</version>
        </dependency>

    </dependencies>

2.配置

点击查看代码
spring.datasource.url=jdbc:mysql://localhost:3306/mall_ams?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=admin
mybatis.mapper-locations=classpath:mapper/*.xml
#开启日志权限
logging.level.com.example.springsecurity.demo=trace
knife4j.enable=true

3.自定义UserDetails

点击查看代码
package com.example.springsecurity.demo.config;

import com.example.springsecurity.demo.entity.User;
import com.example.springsecurity.demo.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;

/**
 * @Author QingHao
 * @Date: 2022/06/05/ 16:24
 * @Describe
 */
@Configuration
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private UserMapper mapper;

    @Override
    public UserDetails loadUserByUsername(String s) throws UsernameNotFoundException {

        //调用mapper接口查询数据
        User usernamePassword = mapper.getByUsername(s);
        //根据mapper查询的数据判断当前用户是否存在
        if (usernamePassword == null) {
            throw new RuntimeException("用户名或密码错误");
        }
        //将要返回的UserDetails对象设置好并返回
        return org.springframework.security.core.userdetails.User.builder()
                .username(s)
                .password(usernamePassword.getPassword())
                .accountExpired(false)
                .accountLocked(false)
                .credentialsExpired(false)
                .authorities("权限待定")
                .build();
    }

}

4.自定义WebSecurityConfigurerAdapter

点击查看代码
package com.example.springsecurity.demo.config;

import com.example.springsecurity.demo.filter.JwtAuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * @Author QingHao
 * @Date: 2022/06/05/ 16:32
 * @Describe
 */
@Configuration
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {


    @Autowired
    private JwtAuthenticationFilter jwtAuthenticationFilter;

    @Bean
    public PasswordEncoder PasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        //关闭csrf防止攻击
        http.csrf().disable()
                //关闭session
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);

        String[] urls = {
                "/favicon.ico",
                "/doc.html",
                "/**/*.js",
                "/**/*.css",
                "/swagger-resources",
                "/v2/api-docs",
                "/user/login"
        };

        http.authorizeRequests().antMatchers(urls).permitAll()
                .anyRequest().authenticated();

        http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
    }

    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
}

5.login

点击查看代码
package com.example.springsecurity.demo.service.impl;

import com.alibaba.fastjson.JSON;
import com.example.springsecurity.demo.entity.User;
import com.example.springsecurity.demo.mapper.UserMapper;
import com.example.springsecurity.demo.service.IUserLoginService;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.JwtBuilder;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.stereotype.Service;

import java.util.HashMap;

/**
 * @Author QingHao
 * @Date: 2022/06/05/ 16:46
 * @Describe
 */
@Slf4j
@Service
public class UserServiceImpl implements IUserLoginService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserMapper mapper;

    @Override
    public String logIn(String username, String password) {

        log.debug("接收到前端数据,suername:>{},password:>{}", username, password);
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(username, password);
        authenticationManager.authenticate(authenticationToken);
        
        JwtBuilder builder = Jwts.builder();

        User admin = mapper.getByUsername(username);

        HashMap<String, Object> map = new HashMap<>();
        map.put("user", JSON.toJSONString(admin));

        String compact = builder.setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                .setClaims(map)
                 //有效期12小时
                .setExpiration(new Date(System.currentTimeMillis() + 1000 * 60 * 60 * 12))
                .signWith(SignatureAlgorithm.HS256, "asdqweasdaseqweqw")
                .compact();

        return compact;
    }
}

6.过滤器OncePerRequestFilter

点击查看代码
package com.example.springsecurity.demo.filter;

import com.alibaba.fastjson.JSON;
import com.example.springsecurity.demo.entity.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.JwtParser;
import io.jsonwebtoken.Jwts;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @Author QingHao
 * @Date: 2022/06/05/ 17:04
 * @Describe
 */
@Slf4j
@Component
public class JwtAuthenticationFilter extends OncePerRequestFilter {
    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, FilterChain filterChain) throws ServletException, IOException {
        //获取请求头
        String tonck = httpServletRequest.getHeader("Authentication");
        log.debug("请求头>>{}", tonck);
        //判断当前请求头是否存在数据
        if (!StringUtils.hasText(tonck)) {
            filterChain.doFilter(httpServletRequest, httpServletResponse);
            return;
        }

        JwtParser parser = Jwts.parser();

        Claims claims = null;
        User admin = null;
        try {
            //解析jwt
            claims = parser.setSigningKey("asdqweasdaseqweqw").parseClaimsJws(tonck).getBody();
            //获取当前user对应的数据
            Object user = claims.get("user");
            log.debug("获取到Object类型数据>>{}", user);
            //获取到存入的JSON数据后解析为对象
            admin = JSON.parseObject(user.toString(), User.class);
            log.debug("解析完成的数据>>>{}", admin);
        } catch (Exception e) {
            e.printStackTrace();
            httpServletResponse.setContentType("application/json; charset=utf-8");
            httpServletResponse.getWriter().println("非法的请求头");
            return;
        }

        //因为SecurityContextHolder中存入的数据类型为Authentication所以借用实现类存入数据UsernamePasswordAuthenticationToken三参的数据内部带有登录成功的标识
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(admin.getUsername(), null, null);
        //将数据设置给SecurityContextHolder
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        //放行继续执行过滤链
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }
}

posted @ 2022-06-05 22:10  Seasky-null  阅读(108)  评论(0)    收藏  举报