shiro和security
说实话,这两个框架我并没有深入研究过,只是简单使用了他们的功能,这里选择了shiro
,只要有两个方面:
- 配置简单,只需简单添加依赖,实现简单的认证方式即可。
- 轻量级,他的功能虽然没有
security
完善,但是完全满足一般开发情况的权限需求。
配置
添加依赖
<!-- shiro -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-web-starter</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这里说明一下为什么添加spring-boot-starter-aop
,shiro
权限控制有两种方式:注解和url配置,如果想使用注解,必须加spring-boot-starter-aop
,不如注解无效,原因是因为url配置是使用filter实现的,注解是使用切面实现的。
实现realm
/**
* @author Ouyang
* @date 18/12/13 23:30
*/
public class MyRealm extends AuthorizingRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
//null usernames are invalid
if (principalCollection == null) {
throw new AuthorizationException("PrincipalCollection method argument cannot be null.");
}
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
return info;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
ShiroUser shiroUser = new ShiroUser();
shiroUser.setUsername("admin");
shiroUser.setPwd(HexUtil.encryption("password"));
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(shiroUser, shiroUser.getPwd(), getName());
return info;
}
}
实际情况中ShiroUser
应该从数据库查取,我是为了方便,此处自己new
了一个对象。
shiroConfig
@Configuration
public class ShiroConfig {
@Bean
public Realm realm() {
return new MyRealm();
}
@Bean
public static DefaultAdvisorAutoProxyCreator getDefaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator creator = new DefaultAdvisorAutoProxyCreator();
/**
* setUsePrefix(false)用于解决一个奇怪的bug。在引入spring aop的情况下。
* 在@Controller注解的类的方法中加入@RequiresRole注解,会导致该方法无法映射请求,导致返回404。
* 加入这项配置能解决这个bug
*/
creator.setUsePrefix(true);
return creator;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition chain = new DefaultShiroFilterChainDefinition();
// all paths are managed via annotations
chain.addPathDefinition("/**", "anon");
return chain;
}
}
chain.addPathDefinition("/**", "anon");
表示全部请求都无需权限,如果想要,加权限,可以将anon
改成authc
。
全局异常处理
package com.ouyanglol.demo.config.expection;
import com.ouyanglol.demo.entity.Result;
import com.ouyanglol.demo.entity.ResultException;
import com.ouyanglol.demo.entity.ResultStatus;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.ShiroException;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
/**
* 统一异常处理
* @author Ouyang
*/
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(ShiroException.class)
public Result handleShiroException(ShiroException e) {
String eName = e.getClass().getSimpleName();
log.error("shiro执行出错:{}",eName);
return Result.error(ResultStatus.ERROR);
}
@ExceptionHandler(UnauthenticatedException.class)
public Result page401(UnauthenticatedException e) {
log.error("未经授权:{}",e.getMessage());
return Result.error(ResultStatus.UNAUTHORIZED);
}
@ExceptionHandler(UnauthorizedException.class)
public Result page403(UnauthorizedException e) {
log.error("无权限访问:{}",e.getMessage());
return Result.error(ResultStatus.FORBIDDEN);
}
@ExceptionHandler(ResultException.class)
public Result error(ResultException e) {
log.error("无权限访问:{}",e.getMessage());
return Result.error(e.getStatus());
}
@ExceptionHandler(Exception.class)
public Result error(Exception e) {
log.error("系统异常:{}",e.getMessage());
return Result.error(ResultStatus.ERROR);
}
}
这里需要注意一下,使用注解的方式是可以GlobalExceptionHandler
才能拦截,如果是url配置的方式,会直接重定向到登陆页。
登陆测试
package com.ouyanglol.demo.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ouyanglol.demo.entity.Result;
import com.ouyanglol.demo.entity.ResultStatus;
import com.ouyanglol.demo.entity.ShiroUser;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.LockedAccountException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.UnauthenticatedException;
import org.apache.shiro.authz.UnauthorizedException;
import org.apache.shiro.subject.Subject;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Ouyang
* @date 18/12/16 19:16
*/
@RestController
@Slf4j
public class ShiroController {
@RequestMapping("/401")
public Result page401() {
throw new UnauthenticatedException();
}
@RequestMapping("/403")
public Result page403() {
throw new UnauthorizedException();
}
@PostMapping("/login")
public Object login(@RequestBody String body){
String oper = "用户登录";
log.info("{}, body:{}",oper,body);
JSONObject json = JSON.parseObject(body);
String uname = json.getString("uname");
String pwd = json.getString("pwd");
if (StringUtils.isEmpty(uname)){
return Result.error(ResultStatus.FORBIDDEN);
}
if (StringUtils.isEmpty(pwd)){
return Result.error(ResultStatus.FORBIDDEN);
}
Subject currentUser = SecurityUtils.getSubject();
try {
//登录
currentUser.login( new UsernamePasswordToken(uname, pwd) );
//从session取出用户信息
ShiroUser user = (ShiroUser) currentUser.getPrincipal();
if (user==null) {
throw new AuthenticationException();
}
//返回登录用户的信息给前台,含用户的所有角色和权限
return Result.success("登陆成功");
} catch ( UnknownAccountException uae ) {
log.warn("用户帐号不正确");
} catch ( IncorrectCredentialsException ice ) {
log.warn("用户密码不正确");
} catch ( LockedAccountException lae ) {
log.warn("用户帐号被锁定");
} catch ( AuthenticationException ae ) {
log.warn("登录出错");
}
return Result.error(ResultStatus.ERROR);
}
}
这里只列举了简单的代码,完整代码请下载项目查看。
项目地址
地址:https://github.com/a252937166/spring-boot-demo
分支:feature/shiro
参考文章
我对shiro
也是初学者水平,就不多讲,免得误人子弟,基本是按照下方文章配置的。