本文介绍在SpringBoot2.6下配置Shiro认证的方法:
1.pom.xml引入依赖
<dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.3.2</version> </dependency>
2.定义 MyAuthorizingRealm 实现用户的认证和权限提取,绑定自己业务的用户服务对象进行处理
public class MyAuthorizingRealm extends AuthorizingRealm { @Autowired private UserService userService = null; //授权 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { String username = (String) principals.getPrimaryPrincipal(); Set<String> permList = new HashSet<>(); //TODO 读取权限的方法,一般通过用户获取角色,读取角色所有拥有的权限,比如user:list,user:save这种标记,存储到permList中。 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); if(permList!=null) info.setStringPermissions(permList); return info; } //自行编码处理验证逻辑 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); String password = new String((char[])token.getCredentials()); if(StringUtils.isEmpty(username)) { throw new UnknownAccountException(); } User user = userService.findByUserName(username); if(user == null){ throw new UnknownAccountException(); } if(password.equals(user.getUserPassword())) { throw new IncorrectCredentialsException(); } return new SimpleAuthenticationInfo(username, password, getName()); }
3.定义Shiro配置,将自定义的realm对象进行绑定(此处还有更多的配置,比如sessionManager,cacheManager等)
@Configuration public class ShiroConfiguration { //将自己的验证方式加入容器 @Bean public CmsAuthorizingRealm myShiroRealm() { MyAuthorizingRealmmyShiroRealm = new MyAuthorizingRealm(); return myShiroRealm; } //权限管理,配置主要是Realm的管理认证 @Bean public org.apache.shiro.mgt.SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); securityManager.setRealm(myShiroRealm()); return securityManager; } @Bean public ShiroFilterFactoryBean shiroFilterFactoryBean(org.apache.shiro.mgt.SecurityManager securityManager) { ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); shiroFilterFactoryBean.setSecurityManager(securityManager); //登录页 shiroFilterFactoryBean.setLoginUrl("/login.html"); return shiroFilterFactoryBean; } //加入注解的使用,不加入这个注解不生效 @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(org.apache.shiro.mgt.SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
4.登录方法,定义LoginController进行登录登出(CommonResult是自定义的一个返回对象)
@PostMapping("/login") @ResponseBody public CommonResult<String> login(@RequestParam("userName") String userName, @RequestParam("password") String password, HttpServletRequest request,ModelMap model){ //做简单的登录的操作 UsernamePasswordToken token = new UsernamePasswordToken(userName,password); Subject subject = SecurityUtils.getSubject(); try { //登录操作,该方法没有抛出异常表示登录成功,接下来就可以根据业务方法获取用户对象,构建session subject.login(token); User user = userService.findByUserName(userName); //设置session request.getSession().setAttribute("user",user); return CommonResult.success(""); } catch(Exception ex){ log.error("登录错误",ex); return CommonResult.error(1,"用户名密码错误"); }
5.登出方法
@RequestMapping("/logout") public void logout(HttpServletRequest request, HttpServletResponse response) throws IOException { Subject subject = SecurityUtils.getSubject(); subject.logout(); request.getSession().setAttribute("user", null); response.sendRedirect("/"); }
6.给方法加上授权认证
此时虽然加了shiro登录认证,但每个controller方法并没有加上权限、认证等检查,需要加上需要对应的权限或认证注解即可
要求必须登录,但不检查具体权限:
@RequiresAuthentication @GetMapping(value = "/list") public CommonResult<List<User>> getUserList(@RequestParam("ids") Collection<Integer> ids) { return CommonResult.success(userService.findAllById(ids)); } @GetMapping(value = "/get") public CommonResult<User> get(Integer userId){ return CommonResult.success(userService.find(userId)); }
此时页面请求get时不需要登录,请求list需要登录才能访问。
下面的get请求必须有content:mgr权限才能够访问,每个用户拥有的权限列表是在MyAuthorizingRealm 加入到permList中的。也可以通过RequiresRoles来检查角色。
@RequiresPermissions("content:mgr") @RequestMapping("get") public void get(HttpServletRequest request,HttpServletResponse response) { ... }
但此时无论是rest接口还是请求的页面,在没有登录时都会跳转到登录界面,rest接口一般希望返回json无权限的提示好让调用方进行处理,页面可以跳转到登录页。
7、定义Shiro异常,针对请求类型返回不同的内容
页面控制器一般用Controller定义,rest接口控制器一般用RestController定义,针对两种Controller进行拦截处理
注意比较关键:其中的Order(1)注解,让RestController优先于Controller的handler先加载,否则因为继承关系,RestController的Handler会不生效。
@Slf4j @RestControllerAdvice(annotations = RestController.class) @Order(1) public class RestControllerExceptionHandler { @ExceptionHandler({Exception.class}) public CommonResult handException(Exception e) { log.error("",e); return CommonResult.error(500,e.toString()); } @ExceptionHandler({UnauthorizedException.class, AuthorizationException.class}) public CommonResult handAuthException(Exception e) { //log.error("",e); return CommonResult.error(401,e.getMessage()); } }
处理Controller页面请求的授权检查异常:
@Slf4j @ControllerAdvice(annotations = Controller.class) @Order(2) public class ControllerExceptionHandler { @ExceptionHandler(value = Exception.class) public String handException(Exception e) { log.error("",e); return "error"; } @ExceptionHandler({ UnauthorizedException.class, AuthorizationException.class }) public String handAuthException(Exception e) { //log.error("",e); //对于页面为授权的直接返回到登录页 return "login"; } }
这两个handler也支持通过package进行匹配,如下:
@ControllerAdvice(basePackages = "com.xxxx.page")
这样满足com.xxx.page下的Controller都会进行匹配进行异常的处理,这样就不用配置Order注解了。
标签:web,return,SpringBoot,登录,RestAPI,class,new,CommonResult,public From: https://www.cnblogs.com/webjlwang/p/16855741.html