shiro+jwt进行认证和授权的解决方案代码实例

shiro+jwt进行认证和授权的解决方案代码实例,第1张

shiro+jwt进行认证和授权的解决方案代码实例

文章目录
  • token类
  • 自定义realm
  • 自定义的jwtFilter用于访问拦截:
  • shiroconfig
  • controller
  • vo对象
  • 测试

jwt和shiro框架就不多介绍了,直接上实例代码吧。目前测试可正常登录、获得用户角色、访问接口时根据@requireRoles进行拦截。

token类
import org.apache.shiro.authc.AuthenticationToken;
//一般的登陆只需要校验账号和密码两个要素,默认的UsernamePasswordToken就能满足需求
//这个就类似UsernamePasswordToken
//在JwtRealm中的授权部分,可以使用JwtUtil.decode(jwt).get("username")获取到username,使用username去数据库中查找到对应的权限,然后将权限赋值给这个用户就可以实现权限的认证了


public class JWTToken implements AuthenticationToken {

    private String token;

    public JWTToken(String token){
        this.token = token;
    }

    @Override
    public Object getPrincipal() {
        return token;
    }

    @Override
    public Object getCredentials() {
        return token;
    }
}

自定义realm
@Slf4j
public class JwtRealm extends AuthorizingRealm {
    
    @Override
    public boolean supports(AuthenticationToken token) {
        //这个token就是从过滤器中传入的jwtToken
        return token instanceof JWTToken;
    }
    @Autowired
    private IInowRoleUserService roleUserService;
    @Autowired
    private IInowRoleService roleService;
    @Autowired
    private IInowUserService userService;
    //授权
    @Override
    protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
        log.info("=========执行了授权===========================");
        InowUser userName = (InowUser) principalCollection.getPrimaryPrincipal();
        InowUser inowUser = userService.selectOneByName(userName.getUserName());
        SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
        final InowRoleUser roleuser = roleUserService.getOne(new LambdaQueryWrapper().eq(InowRoleUser::getUserId, inowUser.getUserId()));
        List inowRoleList = new ArrayList<>();
        inowRoleList.add(roleService.getOne(new LambdaQueryWrapper().eq(InowRole::getId,roleuser.getRoleId())).getRoleName());
        simpleAuthorizationInfo.addRoles(inowRoleList);
        //添加用户角色,用于@requireroles注解
        return simpleAuthorizationInfo;
    }

    //认证
    // 这里的 token是从 JWTFilter 的 executeLogin 方法传递过来的(请求头的token)
    // 只要调用了subject.login(token)方法,就会进入到realm的doGetAuthenticationInfo内。
    @Override
    protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken auth) throws AuthenticationException {

       String token = (String)auth.getCredentials();
     log.info("从auth获得的Token"+token);
       String userName = JWTUtil.getUsername(token);

        //用户不存在(这个在登录时不会进入,只有在token校验时才有可能进入)
        if(userName == null)
        {
            throw new UnknownAccountException();
        }
        InowUser user = userService.selectOneByName(userName);
        if(Objects.isNull(user))
        {
            throw new UnknownAccountException();
        }
        String salt = "666666"; //自己随便写的一个加密盐
        Md5Hash md5Hash = new Md5Hash(user.getUserId(), salt, 1024);
if(!JWTUtil.verify(token,userName, md5Hash.toHex()))//学号当做用户密码
        {

            throw new IncorrectCredentialsException();
        }
        //toke过期
        if(JWTUtil.isExpire(token)){
            throw new ExpiredCredentialsException();
        }
     
        return new SimpleAuthenticationInfo(user, token, getName());
    }

}
自定义的jwtFilter用于访问拦截:
@Slf4j
//@Component("jwtFilter")
//去掉@Component注解,让filter不交给springbean管理,因为这会导致shiro内置的anon过滤器失效,具体原因还未找出
public class JwtFilter extends BasicHttpAuthenticationFilter {

    
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        //在请求头中获取token
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader("Authorization"); //前端命名Authorization
        //token不存在
        if(token == null || "".equals(token)){
Result result = new Result();
            result.setCode(400);
            result.setMsg("token为空,无法访问");
            out(response,result);
   return false;

        }

        //token存在,进行验证
       JWTToken jwtToken = new JWTToken(token);
        try {
            SecurityUtils.getSubject().login(jwtToken);  //通过subject,提交给myRealm进行登录验证
            return true;
        } catch (ExpiredCredentialsException e){
            Result result = new Result();
            result.setCode(400);
            result.setMsg("登录已经过期");
out(response,result);
            e.printStackTrace();
            return false;
        } catch (ShiroException e){
            // 其他情况抛出的异常统一处理,由于先前是登录进去的了,所以都可以看成是token被伪造造成的
         Result res = new Result();



            res.setMsg("无效token");
            out(response,res);
            e.printStackTrace();
            return false;
        }
    }

    
    private void out(ServletResponse response, Result res) throws IOException {
        HttpServletResponse httpServletResponse = WebUtils.toHttp(response);
        ObjectMapper mapper = new ObjectMapper();
        String jsonRes = mapper.writevalueAsString(res);
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setContentType("application/json; charset=utf-8");
        httpServletResponse.getOutputStream().write(jsonRes.getBytes());
    }

    
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        try {
            return executeLogin(request, response);  //token验证
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    
//    @Override
//    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
//        return super.onAccessDenied(request, response);
//    }

    
//    @Override
//    protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request, ServletResponse response) throws Exception {

//    }
}

shiroconfig
@Configuration
public class ShiroConfig {

    public logOutFilter mylogoutFilter()
    {
        logOutFilter MylogoutFilter = new logOutFilter();
        MylogoutFilter.setLoginUrl("login");//设置退出后重定向的跳转地址
        return MylogoutFilter;
    }

    
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(@Autowired @Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager,
                                                         @Autowired ShiroFilterChainDefinition definition
                                                       ){
        JwtFilter jwtFilter = new JwtFilter();
        ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
        bean.setSecurityManager(defaultWebSecurityManager);//设置安全管理器
        //可以添加shiro的内置过滤器
        //anon:无需认证就可以访问 authc:必须认证了才能用 oerms:拥有对某个资源的权限才能访问
        bean.setLoginUrl("/login");//设置登录路径
        //bean.setUnauthorizedUrl("/unlogin");

        Map filterMap = new HashMap<>();
        // 注销成功,则跳转到指定页面
        filterMap.put("logout", mylogoutFilter());
        filterMap.put("anon", new AnonymousFilter());

        filterMap.put("jwt",jwtFilter);
bean.setFilters(filterMap);

   

 bean.setFilterChainDefinitionMap(definition.getFilterChainMap());
        return bean;
    }

    
    //@Bean
    public JwtFilter jwtFilter(){
        return new JwtFilter();
    }


    
    @Bean
    public ShiroFilterChainDefinition shiroFilterChainDefinition(){
        DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
        Map map = new linkedHashMap<>();
        map.put("/login","anon");
        map.put("/swagger-ui.html","anon");
        map.put("/doc.html","anon");
        map.put("
    @Bean
    public JwtRealm myUserRealm(){
        JwtRealm userRealm = new JwtRealm();
        return userRealm;
    }

    
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(@Autowired DefaultWebSecurityManager securityManager){
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
    @Bean
    public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
        return new LifecycleBeanPostProcessor();
    }

    @Bean
    @DependsOn("lifecycleBeanPostProcessor")
    //DefaultAdvisorAutoProxyCreator:
    //BeanPostProcessor实现,它根据当前BeanFactory中的所有候选Advisor创建 AOP 代理。
  
    public DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        // 强制指定注解的底层实现使用 cglib 方案,防止重复代理和可能引起代理出错的问题
        defaultAdvisorAutoProxyCreator.setProxyTargetClass(true);
        return defaultAdvisorAutoProxyCreator;
    }
    

        
    
    @Bean
    public SubjectFactory subjectFactory() {
        return new JwtDefaultSubjectFactory();
    }

    @Bean("securityManager")
    public DefaultWebSecurityManager securityManager(@Autowired JwtRealm myRealm) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(myRealm);
        // 关闭 ShiroDAO 功能
        DefaultSubjectDAO subjectDAO = new DefaultSubjectDAO();
        DefaultSessionStorageevaluator defaultSessionStorageevaluator = new DefaultSessionStorageevaluator();
        // 不需要将 Shiro Session 中的东西存到任何地方(包括 Http Session 中)
        defaultSessionStorageevaluator.setSessionStorageEnabled(false);
        subjectDAO.setSessionStorageevaluator(defaultSessionStorageevaluator);
        securityManager.setSubjectDAO(subjectDAO);
        //禁止Subject的getSession方法
        securityManager.setSubjectFactory(subjectFactory());
        return securityManager;
    }
    }
controller
  @ApiOperation("登录接口")
    @GetMapping
    public Result userLogin(@ApiParam(name="userId",value="用户学号",required=true)
                            @RequestParam(value = "userId",required = true)String userId,
                            @ApiParam(name="userName",value="用户姓名",required=true)
    @RequestParam(value = "userName",required = true)String userName) {

        // 1、获取Subject实例对象


        //根据用户名获取正确用户信息
    InowUser user = userService.selectOneByName(userName);
        if(user == null)
        {
            return Result.fail("无效用户,请检查您的姓名");
        }

        //盐 + 输入的密码(注意不是用户的正确密码) + 1024次散列,作为token生成的密钥
        String salt = "666666";
        Md5Hash md5Hash = new Md5Hash(userId, salt, 1024);

        //生成token字符串
        String token = JWTUtil.getJwtToken(userName, md5Hash.toHex());   //toHex转换成16进制,32为字符
        log.info("从controller生成的Token"+token);
        JWTToken jwtToken = new JWTToken(token);


        Subject currentUser = SecurityUtils.getSubject();


            // 4、认证
            try {
                currentUser.login(jwtToken);// 传到Realm类中的方法进行认证 进入 AuthenticationInfo方法


                //session.setAttribute("username", userName);
                InowUser nowUser = (InowUser) currentUser.getPrincipal();
                InowUserVo userVo = new InowUserVo();
                BeanUtils.copyProperties(nowUser,userVo);
                userVo.setToken(token);
                return Result.success(userVo);
            }
            catch (AuthenticationException e) {
                String msg = null;

                if (StringUtils.isNotEmpty(e.getMessage())) {
                   msg = e.getMessage();
                }
                return Result.fail("用户名与学号不匹配!");
            }
 //   }
    }
vo对象

在vo中放了token属性的成员属性

@Data
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public class InowUserVo implements Serializable {
    private static final long serialVersionUID = 1L;
    
    @NotBlank(message = "id不能为空!")
    @ApiModelProperty(value="用户id,学号或教师号",name="id",example="2020214283")
    private String userId;
    private Integer id;
    
    @NotBlank(message = "姓名不能为空!")
    @ApiModelProperty(value="用户姓名",name="userName",example="张三")
    private String userName;

    
    @ApiModelProperty(value="用户学院",name="userAcademy",example="计算机与信息学院")
    private String userAcademy;

    
    @ApiModelProperty(value="微信id",name="wechatId",example="微信id")
    private Long wechatId;

    @ApiModelProperty(value="性别",name="userSex",example="1男,2女")
    private byte userSex;
    @ApiModelProperty(value="最后登陆时间",name="loginDate")
    private Date loginDate;
    @ApiModelProperty(value="用户角色",name="roleName")
    private String roleName;
private String token;
}

测试

登录接口:

访问一个标有注解@RequireRoles(“admin”)的方法:
在不加token时返回结果:

加一个没有Admin角色的Token访问结果:

用有admin角色的用户token访问结果:

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5637109.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-16
下一篇 2022-12-16

发表评论

登录后才能评论

评论列表(0条)

保存