SpringBoot整合Shiro安全框架。
org.apache.shiro
shiro-core
1.4.0
org.apache.shiro
shiro-spring
1.4.0
package com.plf.springbootexample.realm;
import java.util.HashSet;
import java.util.Set;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.SimpleAuthenticationInfo;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
public class MyShiroRealm extends AuthorizingRealm{
/**
* 认证、登录
*/
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
//1、把AuthenticationToken强转成UsernamePasswordToken
UsernamePasswordToken upToken = (UsernamePasswordToken) token;
//2、从UsernamePasswordToken中获取username
String username = upToken.getUsername();
//3、调用数据库方法,从数据库中查询username对应的用户记录
System.out.println("从数据库中获取用户:"+username);
//4、根据用户情况,来构建AuthenticationInfo对象并返回
//以下信息是从数据库中获取的
//1) principal 认证的实体信息 可以是username 也可以是数据表对应的用户的实体类对象
Object principal = username;
//2) credentials 密码 明文123456
Object credentials="fc1709d0a95a6be30bc5926fdb7f22f4";
//3) realmName 当前realm对象的name 调用父类的getName()方法即可
String realmName = getName();
//4)盐值
ByteSource credentialsSalt =null;
SimpleAuthenticationInfo info = new SimpleAuthenticationInfo(principal,
credentials,credentialsSalt,realmName);
return info;
}
/**
* 授权
*/
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
//从PrincipalCollection中获取登录用户的信息
Object principal = principals.getPrimaryPrincipal();
//利用登录的用户信息来确定当前用户的角色和权限
//从数据库中获取到用户的权限
Set roles = new HashSet<>();
roles.add("user");
if("admin".equals(principal)){
roles.add("admin");
}
//创建SimpleAuthorizationInfo 并设置其roles属性
SimpleAuthorizationInfo info =new SimpleAuthorizationInfo(roles);
//返回SimpleAuthorizationInfo对象
return info;
}
}
package com.plf.springbootexample.config;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import java.util.LinkedHashMap;
import java.util.Map;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.plf.springbootexample.realm.MyShiroRealm;
@Configuration
public class ShiroConfig {
/**
* 负责shiroBean的生命周期
* @return
*/
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor(){
return new LifecycleBeanPostProcessor();
}
/**
* 自定义的认证类,用于用户的认证、登录、授权
* @return
*/
@Bean
public MyShiroRealm myShiroRealm(HashedCredentialsMatcher hashedCredentialsMatcher){
MyShiroRealm myShiroRealm = new MyShiroRealm();
myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher);
return myShiroRealm;
}
@Bean
public SecurityManager securityManager(MyShiroRealm myShiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm);
return securityManager;
}
/**
* 配置Shiro的MD5加密
*/
@Bean
public HashedCredentialsMatcher hashedCredentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
//指定加密方式为MD5
credentialsMatcher.setHashAlgorithmName("MD5");
//加密次数
credentialsMatcher.setHashIterations(1024);
//credentialsMatcher.setStoredCredentialsHexEncoded(true);
return credentialsMatcher;
}
@Bean
public ShiroFilterFactoryBean shirFilter(SecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
//拦截器
Map filterChainDefinitionMap = new LinkedHashMap();
//anon 所有url都可以匿名访问
//authc 所有url都必须认证通过才可以访问
//user 配置记住我活着认证通过才可以访问
//logout 退出登录
// 配置不会被拦截的链接 顺序判断
filterChainDefinitionMap.put("/static/**", "anon");
//配置退出 过滤器,其中的具体的退出代码Shiro已经替我们实现了
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/shiro/index", "anon");
filterChainDefinitionMap.put("/shiro/login", "anon");
filterChainDefinitionMap.put("/shiro/user", "authc,roles[user]");
filterChainDefinitionMap.put("/shiro/admin", "authc,roles[admin]");
// 过滤连接自定义,从上往下顺序执行,所以使用LinkHashMap /**放在最下面
filterChainDefinitionMap.put("/**", "authc");
shiroFilterFactoryBean.setLoginUrl("/shiro/index");
// 登录成功后要跳转的链接
shiroFilterFactoryBean.setSuccessUrl("/shiro/success");
//未授权界面;
shiroFilterFactoryBean.setUnauthorizedUrl("/shiro/fail");
shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return shiroFilterFactoryBean;
}
//启用Shiro的注解
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator defaultAAP
= new DefaultAdvisorAutoProxyCreator();
defaultAAP.setProxyTargetClass(true);
return defaultAAP;
}
@Bean
public AuthorizationAttributeSourceAdvisor
authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor
= new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
}
因为考虑到Shiro出现无权限等情况会报异常,这样对整个项目不友好,就是自己定义全局异常处理。
import javax.servlet.http.HttpServletRequest;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ControllerAdvice
public class GlobalDefaultExceptionHandler {
@ExceptionHandler(Exception.class)
public String defaultExceptionHandler(HttpServletRequest req,Exception e){
if(e instanceof UnauthorizedException){
return "noperssion";
}
return "defaultException";
}
}
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.stereotype.Repository;
@Repository
public class LoginService {
//admin权限才能访问
@RequiresRoles("admin")
public void ShiroService(){
System.out.println("======ShiroService======");
}
}
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.plf.springbootexample.service.LoginService;
@Controller
@RequestMapping("shiro")
public class LoginController {
@Autowired
private LoginService loginService;
@RequestMapping("testService")
public String testService(){
loginService.ShiroService();
return "success";
}
@RequestMapping("login")
public String Login(@RequestParam String username
,@RequestParam String password){
Subject currentUser = SecurityUtils.getSubject();
if(!currentUser.isAuthenticated()){
//把用户名和密码封装为UsernamePasswordToken对象
UsernamePasswordToken token = new UsernamePasswordToken(username,password);
token.setRememberMe(true);
try{
currentUser.login(token);
}catch(AuthenticationException e){
System.out.println("登录失败:"+e.getMessage());
return "fail";
}
}
return "success";
}
}