springsecurity整合完成认证服务

1.pom加依赖

     <!--springsecurity-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <!--jwt-->
        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.9.1</version>
        </dependency>

2.securityconfig

        咱这个认证服务就是颁发token的,所有咱们放行所有springsecurity的认证。然后自己来通过调用auth来认证。如果没有认证通过,则通过handler来返回错误信息

        然后就是给auth设置好userdetailservie和密码校验的那个BCryptPasswordEncoder

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Autowired
    private JwtTokenProvider jwtTokenProvider;
    @Autowired
    private JwtAuthenticationEntryPoint unauthorizedHandler;
    @Autowired
    private CustomUserDetailsService customUserDetailsService;
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable().
                authorizeRequests().
                antMatchers("/**").permitAll().
                and().exceptionHandling().authenticationEntryPoint(unauthorizedHandler).
                and().sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
    }

    /**
     * 如果你想使用AuthenticationManager这个玩意进行显示的校验用户名密码那么就需要自己手动注入一下,如果不显示的使用校验,
     * springsecurity会自己注入并完成使用,因为咱们在生成token的接口里面用到了这个校验了,所以咱们注入一下
     *
     * @return
     * @throws Exception
     */
    @Bean
    @Override
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

    /**
     * 进行密码加密校验。通常你配置了这个玩意,你数据库的密码是加密的,你传入过来的密码会通过这个玩意加密再匹配
     *
     * @return
     */
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 它的主要作用是配置全局的身份验证管理器 AuthenticationManagerBuilder
     * 就是配置UserDetailsService和BCryptPasswordEncoder
     *
     * @param auth
     * @throws Exception
     */
    @Autowired
    public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(customUserDetailsService).passwordEncoder(passwordEncoder());
    }
}

3.service 

@Service
public class AccountServiceImpl implements AccountService {
    Logger logger = LoggerFactory.getLogger(AccountServiceImpl.class);
    @Autowired
    private ConsumerClient consumerClient;
    @Autowired
    private AccountClient accountClient;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtTokenProvider jwtTokenProvider;

    @Override
    public ResponseResult<String> register(AccountDTO accountDTO) {
        //1.判断必要参数
        if (StrUtil.isBlank(accountDTO.getEmail()) || StrUtil.isBlank(accountDTO.getVeriCode())) {
            throw new RuntimeException("参数异常");
        }
        //2.获取是否注册过
        Integer num = accountClient.getAccountByEmail(accountDTO.getEmail());
        logger.info("{} if exist num==>{}", accountDTO.getEmail(), num);
        //3.进行注册or登录-
        //3.1 -登录就是返回一个token
        Authentication authentication = authenticationManager.authenticate(
                new UsernamePasswordAuthenticationToken(
                        accountDTO.getEmail(),
                        accountDTO.getPassword()
                )
        );
        SecurityContextHolder.getContext().setAuthentication(authentication);

        String token = jwtTokenProvider.createToken(accountDTO.getEmail(), new ArrayList<>());
        // 3.2 todo 没有注册则进行注册,注册后返回token,需要一个默认密码

        return ResponseResult.okResult(token);
    }
}

authenticationManager.authenticate() 传入我们的用户名密码,然后springsecurity就会帮我们往下进行验证

主要流程咱们画个图

4.CustomUserDetailsService

上面调用的方法最终就会找到这个loadUserByUsername()方法

@Service
public class CustomUserDetailsService implements UserDetailsService {
    Logger logger = LoggerFactory.getLogger(CustomUserDetailsService.class);
    @Autowired
    private AccountClient accountClient;
    /**
     * 这部分是核心,会在authenticate调用的时候执行该方法
     *
     * @param username the username identifying the user whose data is required.
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        AccountVO account = accountClient.findByUsername(username);
        logger.info("query account is {}", JSON.toJSONString(account));
        // 这里就是进行查询用户并判断是否存在
        if (account == null) {
            throw new UsernameNotFoundException("User not found");
        }
        return UserPrincipal.create(account);
    }
}

然后我们再看这个方法的返回。他要求你必须是一个UserDetails类,我们想要灵活配置就自己实现UserDetails

5.UserDetails

这个里面有todo,就是可以在创建这个对象的时候给设置上值。这样就可以根据我们数据库查询的accountvo来设置,这样springsecurity就根据后面的方法来获取值,判断你的账号是否可用之类的

/**
 * 这个类也是在authenticate校验时进行各种判断,什么过期,是否可用的账号之类的
 */
public class UserPrincipal implements UserDetails, Serializable {
    private Long id;
    private String email;
    private String password;
    private Set<GrantedAuthority> authorities;
    public UserPrincipal() {
    }
    public UserPrincipal(Long id, String email, String password, Set<GrantedAuthority> authorities) {
        this.id = id;
        this.email = email;
        this.password = password;
        this.authorities = authorities;
    }
    public static UserPrincipal create(AccountVO accountVO) {
        Set<GrantedAuthority> authorities = new HashSet<>();
//                user.getRoles().stream().map(role ->
//                new SimpleGrantedAuthority(role.getName().name())
//        ).collect(Collectors.toList());
        return new UserPrincipal(
                accountVO.getId(),
                accountVO.getEmail(),
                accountVO.getPassword(),
                authorities
        );
    }
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return null;
    }
    @Override
    public String getPassword() {
        return this.password;
    }
    @Override
    public String getUsername() {
        return this.email;
    }
    public void setUsername(String email) {
        this.email = email;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    @Override
    public boolean isAccountNonExpired() {
        // todo
        return true;
    }
    @Override
    public boolean isAccountNonLocked() {
        // todo
        return true;
    }
    @Override
    public boolean isCredentialsNonExpired() {
        // todo
        return true;
    }
    @Override
    public boolean isEnabled() {
        // todo
        return true;
    }
    // ...其他代码,包括getters和实现UserDetails接口的方法...
}

6.JwtTokenProvider

@Component
public class JwtTokenProvider {
    private String secretKey = "secret";

    public String createToken(String username, List<String> roles) {
        Claims claims = Jwts.claims().setSubject(username);
        claims.put("roles", roles);
        Date now = new Date();
        Date validity = new Date(now.getTime() + 3600000); //设置Token有效期为1小时
        return Jwts.builder()
                .setClaims(claims)
                .setIssuedAt(now)
                .setExpiration(validity)
                .signWith(SignatureAlgorithm.HS256, secretKey)
                .compact();
    }
}

7.JwtAuthenticationEntryPoint

/**
 * 这个是一个没有校验通过的错误返回器
 */
@Component
public class JwtAuthenticationEntryPoint implements AuthenticationEntryPoint {
    Logger logger = LoggerFactory.getLogger(JwtAuthenticationEntryPoint.class);

    @Override
    public void commence(HttpServletRequest httpServletRequest,
                         HttpServletResponse httpServletResponse,
                         AuthenticationException e) throws IOException, ServletException {
        logger.error("Responding with unauthorized error. Message - {}", e.getMessage());
        httpServletResponse.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage());
    }
}

  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值