引言:Springboot整合shiro安全框架+Redis+Swagger+Filter
整个工程的大概流程
如果有需要可以私信我要整个工程的源代码附带sql 还有ssm整合所有的框架
gitee仓库位置https://gitee.com/yhlgiteejava/qy165-shiro
1 添加依赖
<dependencies>
<!--shiro和redis整合的依赖-->
<dependency>
<groupId>org.crazycake</groupId>
<artifactId>shiro-redis</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--swagger依赖-->
<dependency>
<groupId>io.github.jianzhichun</groupId>
<artifactId>spring-boot-starter-swagger2</artifactId>
<version>0.0.1</version>
</dependency>
<dependency>
<groupId>com.github.xiaoymin</groupId>
<artifactId>swagger-bootstrap-ui</artifactId>
<version>1.9.6</version>
</dependency>
<!--json依赖-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<!--shrio和thymeleaf集成的扩展依赖,为了能在页面上使用xsln:shrio的标签 -->
<dependency>
<groupId>com.github.theborakompanioni</groupId>
<artifactId>thymeleaf-extras-shiro</artifactId>
<version>2.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!--shiro和springboot整合的依赖-->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring-boot-starter</artifactId>
<version>1.7.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
2 application.properties文件
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql:///shiro?serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root
#日志
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
mybatis-plus.mapper-locations=classpath:/mapper/*.xml
#thymeleaf的视图解析器
spring.thymeleaf.suffix=.html
spring.thymeleaf.prefix=classpath:/templates/
#定义shiro的加密算法
shiro.hashAlgorithmName=MD5
shiro.hashIterations=1024
shiro.loginUrl=/login.html
shiro.map[/login]=anon
shiro.map[/doc.html]=anon
shiro.map[/webjars/**]=anon
shiro.map[/swagger-resources/**]=anon
shiro.map[/v2/**]=anon
shiro.map[/webjars]=anon
shiro.map[/**]=authc
3 需要配置一些文件
(1)添加MyRealm
public class MyRealm extends AuthorizingRealm {
@Autowired
private UserService userService;
@Autowired
private PermissionService permissionService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
User1 user1 = (User1) principalCollection.getPrimaryPrincipal();
List<String> list = permissionService.selectPermissionById(user1.getId());
if (list.size()!=0){
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.addStringPermissions(list);
return info;
}
return null;
}
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
String username = authenticationToken.getPrincipal().toString();
User1 user1 = userService.selectByusername(username);
if (user1!=null){
ByteSource salt = ByteSource.Util.bytes(user1.getSalt());
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(user1,user1.getUserpwd(),salt,this.getName());
return info;
}
return null;
}
}
(2) 需要注入bean配置文件
@Configuration
@EnableConfigurationProperties(ShiroProperties.class)
public class ShiroConfig {
@Bean
public DefaultWebSecurityManager securityManager(){
DefaultWebSecurityManager securityManager=new DefaultWebSecurityManager();
securityManager.setCacheManager(cacheManager());
securityManager.setRealm(myRealm());
return securityManager;
}
@Bean
public RedisCacheManager cacheManager(){
RedisCacheManager cacheManager = new RedisCacheManager();
cacheManager.setRedisManager(redisManager());
return cacheManager;
}
@Bean
public RedisManager redisManager(){
RedisManager manager = new RedisManager();
manager.setHost("172.16.7.220:6379");
manager.setDatabase(2);
return manager;
}
@Bean
public MyRealm myRealm(){
MyRealm myRealm=new MyRealm();
//设置密码加密器
myRealm.setCredentialsMatcher(credentialsMatcher());
return myRealm;
}
private ShiroProperties shiroProperties;
ShiroConfig(ShiroProperties shiroProperties){
this.shiroProperties=shiroProperties;
}
@Bean
public HashedCredentialsMatcher credentialsMatcher(){
HashedCredentialsMatcher credentialsMatcher=new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName(shiroProperties.getHashAlgorithmName());
credentialsMatcher.setHashIterations(shiroProperties.getHashIterations());
return credentialsMatcher;
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean factoryBean(){
ShiroFilterFactoryBean shiroFilterFactoryBean=new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager());
shiroFilterFactoryBean.setLoginUrl(shiroProperties.getLoginUrl());
//设置过滤规则
/*Map<String,String> map=new HashMap<>();
map.put("/login","anon");
map.put("/**","authc");
map.put("/webjars/**","anon");
map.put("/swagger-resources/**","anon");
map.put("/v2/**","anon");
map.put("/doc.html","anon");*/
shiroFilterFactoryBean.setFilterChainDefinitionMap(shiroProperties.getMap());
HashMap<String, Filter> hashMap = new HashMap<>();
hashMap.put("authc",new MyFilter());
shiroFilterFactoryBean.setFilters(hashMap);
return shiroFilterFactoryBean;
}
//springboot如何注册web三大组件。
@Bean
public FilterRegistrationBean<Filter> filterRegistrationBean(){
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setName("shiroFilter");
registrationBean.setFilter(new DelegatingFilterProxy());
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
/**
* 这里是为了能在html页面引用shiro标签,
*/
@Bean(name = "shiroDialect")
public ShiroDialect shiroDialect() {
return new ShiroDialect();
}
}
(4)Swagger配置
@Configuration //表示类似与配置文件
public class SwaggerConfig {
@Bean //加在方法上,表示吧方法的返回结果交于spring容器来管理该对象. 里面封装了接口文档的信息,
public Docket docket(){
Docket docket=new Docket(DocumentationType.SWAGGER_2)
.groupName("qy165")
.apiInfo(getInfo())
.select()
//只为com.aaa.controller包下的类生成接口文档
.apis(RequestHandlerSelectors.basePackage("com.aaa.controller"))
.build();
return docket;
}
private ApiInfo getInfo(){
Contact DEFAULT_CONTACT = new Contact("xxx", "http://www.s66.com", "[email protected]");
ApiInfo apiInfo= new ApiInfo("管理系统API文档", "管理系统API文档",
"1.5", "localhost:8081/doc.html", DEFAULT_CONTACT, "AAA", "http://www.aaa.com");
return apiInfo;
}
}
(5)捕获异常的配置
@ControllerAdvice
@Slf4j
public class MyExceptionHandler {
@ExceptionHandler(AuthorizationException.class)
@ResponseBody
public Result authorizationException(AuthorizationException e){
log.info("权限不足,请联系管理员");
return new Result(403,"权限不足,请联系管理员",null);
}
}
(6)需要定义一个自定义过滤器
public class MyFilter extends FormAuthenticationFilter {
@Override
protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
Result result = new Result(401, "未登录,请先登陆", null);
String string = JSON.toJSONString(result);
writer.print(string);
writer.flush();
writer.close();
return false;
}
}
(7)两个vo实体类
@Data
@ApiModel(value = "登陆表单对象")
public class LoginVo {
@ApiModelProperty(value = "用户名")
private String username;
@ApiModelProperty(value = "密码")
private String password;
}
@Data
@Component
@ConfigurationProperties(prefix = "shiro")
public class ShiroProperties {
private String hashAlgorithmName;
private Integer hashIterations;
private String loginUrl;
private Map<String,String> map;
}
(8)Result返回给前端的JSON数据
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Result {
private Integer code;
private String msg;
private Object data;
}
(9)别忘了在启动类上面加上@MapperScan
3 编写程序逻辑代码
(1)Controller层
@RestController
@Api(tags = "登陆类接口")
public class LoginController {
@PostMapping("/login")
@ApiOperation("登陆接口测试")
public Result login(LoginVo loginVo) {
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(loginVo.getUsername(), loginVo.getPassword());
try {
subject.login(token);
return new Result(200,"登陆成功",null);
} catch (Exception e) {
e.printStackTrace();
return new Result(500,"账号或密码错误",null);
}
}
}
@RestController
@RequestMapping("/user")
@Api(tags = "权限类接口")
public class PermissionController {
@GetMapping("/query")
@RequiresPermissions(value = "user:query")
@ApiOperation("查询权限测试")
public String query(){
return "user:update------------------------";
}
@GetMapping("/update")
@RequiresPermissions(value = "user:update")
@ApiOperation("修改权限测试")
public String update(){
return "user:update------------------------";
}
@GetMapping("/delete")
@RequiresPermissions(value = "user:delete")
@ApiOperation("删除权限测试")
public String delete(){
return "user:delete------------------------";
}
@GetMapping("/insert")
@RequiresPermissions(value = "user:insert")
@ApiOperation("添加权限测试")
public String insert(){
return "user:insert------------------------";
}
@GetMapping("/export")
@RequiresPermissions(value = "user:export") //该注解不能别识别
@ApiOperation("导出权限测试")
public String export(){
return "user:export------------------------";
}
}
(2)Service层
public interface UserService {
User1 selectByusername(String username);
}
public interface PermissionService {
List<String> selectPermissionById(Integer userid);
}
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserDao userDao;
@Override
public User1 selectByusername(String username) {
QueryWrapper<User1> wrapper = new QueryWrapper<>();
wrapper.eq("username",username);
User1 user1 = userDao.selectOne(wrapper);
return user1;
}
}
@Service
public class PermissionServiceImpl implements PermissionService {
@Resource
private PermissionDao permissionDao;
@Override
public List<String> selectPermissionById(Integer userid) {
List<Permission> list = permissionDao.selectPermissionById(userid);
List<String> collect = list.stream().map(Permission::getPercode).collect(Collectors.toList());
return collect;
}
}
(3)Dao层
public interface UserDao extends BaseMapper<User1> {
}
public interface PermissionDao {
List<Permission> selectPermissionById(Integer userid);
}
(4)两个实体类
@Data
@TableName(value = "user")
public class User1 implements Serializable {
@TableId
private Integer id;
private String username;
private String userpwd;
private String sex;
private String address;
private String salt;
}
@Data
@TableName(value = "user")
public class User1 implements Serializable {
@TableId
private Integer id;
private String username;
private String userpwd;
private String sex;
private String address;
private String salt;
}
(5)sql语句mapper
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.aaa.dao.PermissionDao">
<select id="selectPermissionById" resultType="com.aaa.entity.Permission">
select p.*
from shiro.user_role ur
join shiro.role_permission rp on ur.roleId = rp.roleid
join shiro.permission p on rp.perid = p.perid
where ur.userid = #{userId}
</select>
</mapper>