登录模块 用户认证 SpringSecurity +Oauth2+Jwt流程分析

认证

Oauth实现微信登录流程

安全认证:你是谁,能干什么

第三方登录授权过程



1. 用户访问第三方资源,第三方请求微信服务器发送授权请求给用户

2. 用户确认授权后,返回授权码给第三方

3. 第三方拿到授权码后,携带授权码向微信申请令牌

4. 第三方拿到令牌后,携带令牌向微信请求用户的基本信息

5. 微信资源服务器根据访问令牌,返回给第三方用户的基本信息

3-5的交互过程用户看不到,用户只能看到登录成功,显示自己的基本信息


账号密码注册登录

注册时候前端可以先加密,后端再加密一次,这样如果请求被拦截了也不会显示明文密码。

账号密码登录

1. 账号密码登录,网关对于登录和一些静态资源是放行的,可以访问。然后这里是验证登录和生成令牌的过程,这里采用jwt的方式经过RSA的私钥生成令牌,公钥去解析。

2. 在认证服务的过程中会去链接数据库,访问两个数据库,一个是通过fegin的远程调用访问user,role,perssion表(都是多对多),这样返回的时候就会携带用户和权限信息,一个是oauth_client_details(配置数据库链接,自动访问)拿到第三方授权的账号密码(自关联)。有用户名密码查询正确,把uid和生成令牌的jti形成键值对放入cookie中,redis存放的是jti和对应的token信息,这样就能通过cookie中的uid找到jti,再找到redis中的token可以进行解析了

3. 当用户访问服务的时候,会经过微服务网关,这里有两种配置解析的方式。

1.公钥存放在网关,判断cookie,redis中的指定值是否存在,最后通过公钥去解析令牌是否合法,如果都没问题就放行

2.如果私钥配置在微服务上的话,网关判断cookie和redis非空后,设置一下请求的响应头

request.mutate().header("Authorization","Bearer "+jwt);

这就是令牌的传递,这样其他微服务就不需要再去cookie和redis去找令牌了,过来的时候就携带了


现在就分为认证服务资源服务

登录授权后如果有改变权限或者增加微服务地址怎么动态的增加呢?

可以在后台维护一张字典表,专门用作配置,比如要令牌资源路径,或者订单状态,维护一对多的两张表,保存在DB中,取完保存在redis中,用Quartz定时组件来完成定时的更新。


权限控制


用户->角色->权限


1.类方式

···java

/*****

* 自定义授权认证类

*/

@Service

public class UserDetailsServiceImpl implements UserDetailsService {

@Autowired

    ClientDetailsService clientDetailsService;

@Autowired

    private UserFeign userFeign;

/****

    * 自定义授权认证

    * @param username

    * @return

    * @throws UsernameNotFoundException

    */

    @Override

    public UserDetails loadUserByUsername(String username)throws UsernameNotFoundException {

//取出身份,如果身份为空说明没有认证

        Authentication authentication =SecurityContextHolder.getContext().getAuthentication();

//没有认证统一采用httpbasic认证,httpbasic中存储了client_id和client_secret,开始认证client_id和client_secret

        if(authentication==null){

ClientDetails clientDetails =clientDetailsService.loadClientByClientId(username);

if(clientDetails!=null){

//秘钥

                String clientSecret =clientDetails.getClientSecret();

//静态方式

                //return new User(username,new BCryptPasswordEncoder().encode(clientSecret), AuthorityUtils.commaSeparatedStringToAuthorityList(""));

                //数据库查找方式

                return new User(username,clientSecret,AuthorityUtils.commaSeparatedStringToAuthorityList(""));

}

}

if (StringUtils.isEmpty(username)) {

return null;

}

//根据用户名查询用户信息

        //String pwd = new BCryptPasswordEncoder().encode("itheima");

        com.changgou.user.pojo.User user =userFeign.findUserInfo(username);

//创建User对象

        String permissions ="salesman,accountant,user";

UserJwt userDetails =new UserJwt(username,user.getPassword(),AuthorityUtils.commaSeparatedStringToAuthorityList(permissions));

return userDetails;

}

}

···

2.注解方式

ResourceServerConfig类上添加@EnableGlobalMethodSecurity注解,用于开启@PreAuthorize的支持

···java

@Configuration

@EnableResourceServer

@EnableGlobalMethodSecurity(prePostEnabled =true, securedEnabled =true)//激活方法上的PreAuthorize注解

public class ResourceServerConfig extends ResourceServerConfigurerAdapter {

//公钥

    private static final String PUBLIC_KEY ="public.key";

/***

    * 定义JwtTokenStore

    * @param jwtAccessTokenConverter

    * @return

*/

    @Bean

    public TokenStore tokenStore(JwtAccessTokenConverter jwtAccessTokenConverter) {

return new JwtTokenStore(jwtAccessTokenConverter);

}

/***

    * 定义JJwtAccessTokenConverter

* @return

*/

    @Bean

    public JwtAccessTokenConverter jwtAccessTokenConverter() {

JwtAccessTokenConverter converter =new JwtAccessTokenConverter();

converter.setVerifierKey(getPubKey());

return converter;

}

/**

    * 获取非对称加密公钥 Key

    * @return 公钥 Key

*/

    private String getPubKey() {

Resource resource =new ClassPathResource(PUBLIC_KEY);

try {

InputStreamReader inputStreamReader =new InputStreamReader(resource.getInputStream());

BufferedReader br =new BufferedReader(inputStreamReader);

return br.lines().collect(Collectors.joining("\n"));

}catch (IOException ioe) {

return null;

}

}

/***

    * Http安全配置,对每个到达系统的http请求链接进行校验

    * @param http

    * @throws Exception

    */

    @Override

    public void configure(HttpSecurity http)throws Exception {

//所有请求必须认证通过

        http.authorizeRequests()

//下边的路径放行

                .antMatchers(

"/user/add","/user/load/**").//配置地址放行

                permitAll()

.anyRequest().

authenticated();//其他地址需要认证授权

    }

}

···

接下来可以在方法上加入@PreAuthorize(具体控制)

如果希望一个方法能被多个角色访问,配置:@PreAuthorize("hasAnyAuthority('admin','user')")

如果希望一个类都能被多个角色访问,在类上配置:@PreAuthorize("hasAnyAuthority('admin','user')")

你可能感兴趣的:(登录模块 用户认证 SpringSecurity +Oauth2+Jwt流程分析)