目錄
前提:
先準備一個 springboot+mybatis 的環境:https://blog.csdn.net/weidong_y/article/details/81709391
一、引入 shiro 的包
<!-- shiro 配置 -->
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>1.3.2</version>
</dependency>
二、自定義 realm
MyShiroRealm.java
package com.yyzheng.oa.shiro;
import com.yyzheng.oa.dao.User;
import com.yyzheng.oa.service.PermissionService;
import com.yyzheng.oa.service.RoleService;
import com.yyzheng.oa.service.UserService;
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.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.crypto.hash.Md5Hash;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.util.ByteSource;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class MyShiroRealm extends AuthorizingRealm {
@Autowired
private RoleService roleService;
@Autowired
private PermissionService permissionService;
@Autowired
private UserService userService;
// 角色權限和對應權限添加
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) {
// 獲取登錄用戶名
String userName = (String)principalCollection.getPrimaryPrincipal();
// 查詢用戶的角色信息
Set<String> roles = getRolesByUsername(userName);
// 查詢角色的權限信息
Set<String> permissions = getPermissionsByUserName(userName);
// 設置用戶的角色和權限
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
simpleAuthorizationInfo.setRoles(roles);
simpleAuthorizationInfo.setStringPermissions(permissions);
return simpleAuthorizationInfo;
}
// 根據用戶名字從數據庫中獲取當前用戶的權限數據
private Set<String> getPermissionsByUserName(String userName) {
List<String> list = permissionService.queryPermissionNameByUserName(userName);
if( list != null ){
Set<String> sets = new HashSet<>(list);
return sets;
}else{
return null;
}
}
// 根據用戶名字從數據庫中獲取當前用戶的角色數據
private Set<String> getRolesByUsername(String userName) {
List<String> list = roleService.queryRoleNameByUsername(userName);
if( list != null ){
Set<String> sets = new HashSet<>(list);
return sets;
}else{
return null;
}
}
// 認證
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException {
// 1.從主體傳過來的信息中獲取用戶名
String userName = (String)authenticationToken.getPrincipal();
// 2.通過用戶名到數據庫獲取憑證
String password = getPasswordByUserName(userName);
if( password == null ){
return null;
}
SimpleAuthenticationInfo simpleAuthenticationInfo = new SimpleAuthenticationInfo(userName,password,"myShiroRealm");
return simpleAuthenticationInfo;
}
// 通過用戶名從數據庫中獲取當前用戶的密碼
private String getPasswordByUserName(String userName) {
User user = userService.queryUserByUserName(userName);
if( user != null ){
return user.getPassword();
}else{
return null;
}
}
}
三、shiro 配置
package com.yyzheng.oa.config;
import com.yyzheng.oa.exception.MyExceptionResolver;
import com.yyzheng.oa.shiro.MyShiroRealm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
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 org.springframework.web.servlet.HandlerExceptionResolver;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
// 創建自定義 realm
@Bean
public MyShiroRealm myShiroRealm() {
MyShiroRealm myShiroRealm = new MyShiroRealm();
return myShiroRealm;
}
// 創建 SecurityManager 對象
@Bean
public DefaultWebSecurityManager securityManager() {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(myShiroRealm());
return securityManager;
}
// Filter工廠,設置對應的過濾條件和跳轉條件
@Bean
public ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager) {
ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
shiroFilterFactoryBean.setSecurityManager(securityManager);
Map<String, String> map = new HashMap<>();
// 登出
map.put("/logout", "logout");
// 對所有用戶認證
map.put("/**", "authc");
// 對登錄跳轉接口進行釋放
map.put("/subLogin", "anon");
map.put("/err", "anon");
// 登錄
// 注意:這裏配置的 /login 是指到 @RequestMapping(value="/login")中的 /login
shiroFilterFactoryBean.setLoginUrl("/login");
// 首頁
shiroFilterFactoryBean.setSuccessUrl("/index");
// 錯誤頁面,認證不通過跳轉
shiroFilterFactoryBean.setUnauthorizedUrl("/err");
shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
return shiroFilterFactoryBean;
}
// 加入註解的使用,不加這個,註解不生效
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
return authorizationAttributeSourceAdvisor;
}
// 跟上面的註解配置搭配使用,有時候加了上面的配置後註解不生效,需要加入下面的配置
@Bean
@ConditionalOnMissingBean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator app = new DefaultAdvisorAutoProxyCreator();
app.setProxyTargetClass(true);
return app;
}
}
四、寫一個 controller 測試
@Controller
public class UserController {
@RequestMapping(value = "/login")
public String login(){
return "/login";
}
@RequestMapping(value = "/err")
public String err(){
return "/err";
}
@RequestMapping(value = "/subLogin")
public String subLogin(User user){
Subject subject = SecurityUtils.getSubject();
UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(),user.getPassword());
try{
subject.login(token);
}catch (AuthenticationException e){
e.printStackTrace();
return e.getMessage();
}
return "success";
}
@RequiresRoles(value = "admin")
@RequestMapping(value = "/test")
@ResponseBody
public String test(){
return "123";
}
}
五、無權限的時候直接報錯的錯誤方式
自定義一個異常攔截,自定義跳轉頁面。
MyExceptionResolver.java 攔截下異常。
package com.yyzheng.oa.exception;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
public class MyExceptionResolver implements HandlerExceptionResolver{
public ModelAndView resolveException(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) {
System.out.println("==============異常開始=============");
//如果是shiro無權操作,因爲shiro 在操作auno等一部分不進行轉發至無權限url
if(ex instanceof UnauthorizedException){
// 跳轉到 templates 文件下面找 err.html 頁面
ModelAndView mv = new ModelAndView("err");
return mv;
}
ex.printStackTrace();
ModelAndView mv = new ModelAndView("err");
mv.addObject("exception", ex.toString().replaceAll("\n", "<br/>"));
return mv;
}
}
在shiro中配置一下:
// 自定義異常捕獲註冊
@Bean
public HandlerExceptionResolver solver(){
HandlerExceptionResolver handlerExceptionResolver = new MyExceptionResolver();
return handlerExceptionResolver;
}