对于springboot集成shiro小伙伴们知道要实现吗?今天我们就来了解下该如何在springboot集成shiro。
首先我们需要创建一个简单的springboot项目,如下:
然后引入所需要的pom依赖:
<!-- spring 集成 shiro --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-web</artifactId> <version>1.4.0</version> </dependency> < dependency > <groupId>org.apache.shiro</groupId> < artifactId > shiro - spring < /artifactId> < version > 1.4 .0 < /version> < /dependency>
接下来就是各个类的详细编码:
UserInfo
package com.slf.firstappdemo.framework.shiro.domain; import javax.persistence.*; import java.io.Serializable; import java.util.List; @Entity public class UserInfo implements Serializable { private static final long serialVersionUID = 1 L; @Id @GeneratedValue private long id; //用户ID @Column(unique = true) private String username; //账号 private String name; //名称 private String password; //密码 private String salt; //加密密码的盐,在这里salt主要是用来进行密码加密的,当然也可以使用明文进行编码测试,实际开发中还是建议密码进行加密。 private byte state; //用户状态:1:创建未认证(比如没有激活,没有输入验证码等) 等待验证的用户,1:正常状态,2:用户被锁定。 @ManyToMany(fetch = FetchType.EAGER) //立即从数据库进行加载数据 @JoinTable(name = "SysUserrole", joinColumns = { @JoinColumn(name = "uid") } , inverseJoinColumns = { @JoinColumn(name = "roleId") } ) private List < SysRole > roleList; //getter and setter /** * 密码盐. * @return */ public String getCredentialsSalt() { return this.username + this.salt; } //toString }
SysRole
package com.slf.firstappdemo.framework.shiro.domain; import javax.persistence.*; import java.io.Serializable; import java.util.List; /** * 系统角色实体类; * @version v.0.1 */ @Entity public class SysRole implements Serializable { private static final long serialVersionUID = 1 L; @Id @GeneratedValue private Long id; //编号 private String role; // 角色标识程序中判断使用,如"admin",这个是唯一的: private String description; // 角色描述,UI界面显示使用 private Boolean available = Boolean.FALSE; // 是否可用,如果不可用将不会添加给用户 //角色 -- 权限关系:多对多关系; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "permissionId") }) private List < SysPermission > permissions; // 用户 - 角色关系定义; @ManyToMany @JoinTable(name = "SysUserRole", joinColumns = { @JoinColumn(name = "roleId") }, inverseJoinColumns = { @JoinColumn(name = "uid") }) private List < UserInfo > userInfos; // 一个角色对应多个用户 //getter and setter //toString }
SysPermission
package com.slf.firstappdemo.framework.shiro.domain; import java.io.Serializable; import java.util.List; import javax.persistence.*; /** * 权限实体类; * @version v.0.1 */ @Entity public class SysPermission implements Serializable { private static final long serialVersionUID = 1 L; @Id @GeneratedValue private long id; //主键. private String name; //名称. @Column(columnDefinition = "enum('menu','button')") private String resourceType; //资源类型,[menu|button] private String url; //资源路径. private String permission; //权限字符串,menu例子:role:*,button例子:role:create,role:update,role:delete,role:view private Long parentId; //父编号 private String parentIds; //父编号列表 private Boolean available = Boolean.FALSE; @ManyToMany(fetch = FetchType.EAGER) @JoinTable(name = "SysRolePermission", joinColumns = { @JoinColumn(name = "permissionId") }, inverseJoinColumns = { @JoinColumn(name = "roleId") }) private List < SysRole > roles; //getter and setter //toString() }
UserInfoRepository
package com.slf.firstappdemo.framework.shiro.repository; import com.slf.firstappdemo.framework.shiro.domain.UserInfo; import org.springframework.data.repository.CrudRepository; /** *@Author:Flm *@Description:UserInfo持久化类 *@Date:17:14 2018/3/13 */ public interface UserInfoRepository extends CrudRepository < UserInfo, Long > { //通过username查找用户信息 public UserInfo findByUsername(String username); }
UserInfoServiceImpl
import com.slf.firstappdemo.framework.shiro.domain.UserInfo; import com.slf.firstappdemo.framework.shiro.repository.UserInfoRepository; import com.slf.firstappdemo.framework.shiro.service.UserInfoService; import org.springframework.stereotype.Service; import javax.annotation.Resource; @Service public class UserInfoServiceImpl implements UserInfoService { @Resource private UserInfoRepository userInfoRepository; @Override public UserInfo findByUserName(String username) { System.out.println("UserInfoServiceImpl.findByUserName()"); return userInfoRepository.findByUsername(username); } }
Controller(接口省略)
package com.slf.firstappdemo.framework.shiro.controller; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import javax.servlet.http.HttpServletRequest; import java.util.Map; @Controller @RequestMapping("/shiro") public class ShiroController { @RequestMapping( { "/" , "/index" }) public String index() { return "/framwork/shiro/index"; } @RequestMapping(value = "/login", method = RequestMethod.GET) //跳转页面 public String login() { return "/framwork/shiro/login"; } @RequestMapping(value = "/login", method = RequestMethod.POST) //处理登录 public String doLogin(HttpServletRequest request, Map < String, Object > map) { System.out.println("ShiroController.doLogin()"); //登录失败从request中获取shiro处理的异常信息 //shiroLoginFailure:就是shiro异常类的全名 String exception = (String) request.getAttribute("shiroLoginFailure"); System.out.println("exception=========" + exception); String msg = ""; if (exception != null) { if (UnknownAccountException.class.getName() .equals(exception)) { System.out.println("UnknownAccountException -- > 账号不存在:"); msg = "UnknownAccountException -- > 账号不存在:"; } else if (IncorrectCredentialsException.class.getName() .equals(exception)) { System.out.println("IncorrectCredentialsException -- > 密码不正确:"); msg = "IncorrectCredentialsException -- > 密码不正确:"; } else if ("kaptchaValidateFailed".equals(exception)) { System.out.println("kaptchaValidateFailed -- > 验证码错误"); msg = "kaptchaValidateFailed -- > 验证码错误"; } else { msg = "else >> " + exception; System.out.println("else -- >" + exception); } } map.put("msg", msg); return "/framwork/shiro/login"; } @RequestMapping("/userList") @RequiresPermissions("userInfo:view") public String userInfo() { return "/framwork/shiro/userinfo"; } @RequestMapping("/userAdd") @RequiresPermissions("userInfo:add") public String userAdd() { return "/framwork/shiro/userinfoAdd"; } }
login.html(其他页面省略)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> 错误信息:<h4 th:text="${msg}"></h4> <form action="/shiro/login" method="post"> <p>账号:<input type="text" name="username" value="admin"/></p> <p>密码:<input type="text" name="password" value="123456"/></p> <p><input type="submit" value="登录"/></p> </form> </body> </html>
自定义实现Realm:
此类为身份验证的核心类,需要继承AuthorizingRealm类,且实现两个方法
(1)doGetAuthenticationInfo(AuthenticationToken authenticationToken) 获取认证信息,用来验证身份信息。
(2)doGetAuthorizationInfo(PrincipalCollection principalCollection) 获取授权信息,用来进行权限验证。
package com.slf.firstappdemo.framework.shiro.realm; import com.slf.firstappdemo.framework.shiro.domain.SysPermission; import com.slf.firstappdemo.framework.shiro.domain.SysRole; import com.slf.firstappdemo.framework.shiro.domain.UserInfo; import com.slf.firstappdemo.framework.shiro.service.UserInfoService; 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.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import javax.annotation.Resource; /** * @Author:Flm * @Description:身份验证核心类 * @Date:17:21 2018/3/13 */ public class MyShiroRealm extends AuthorizingRealm { @Resource private UserInfoService userInfoService; /** * @Author:Flm * @Description:认证信息(身份验证) * @Date:17:23 2018/3/13 */ @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { System.out.println("MyShiroRealm.doGetAuthenticationInfo()"); //获取用户的输入账号 String username = (String) authenticationToken.getPrincipal(); System.out.println(authenticationToken.getCredentials()); //通过username从数据库中查找user对象 //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro也是有时间间隔机制,2分钟内不会重复执行该方法 UserInfo userInfo = userInfoService.findByUserName(username); System.out.println("----->>userInfo=" + userInfo); if (userInfo == null) { return null; } /** * 获取权限信息:这里没有进行实现 * 请自行根据UserInfo,Role,Permission进行实现 * 获取之后可以在前端for循环显示所有链接 */ //userInfo.setPermissions(userService.findPermissions(user)); //账号判断 //加密方式 //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo, //用户名 userInfo.getPassword(), //密码 ByteSource.Util.bytes(userInfo.getCredentialsSalt()), //salt=username+salt getName() //realm name ); //明文,若存在,则将此用户存放到登录认证info中,无需自己做密码对比,shiro会为我们进行密码对比校验 //SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(userInfo,userInfo.getPassword(),getName()); return authenticationInfo; } /** * 此方法调用 hasRole,hasPermission的时候才会进行回调. * * 权限信息.(授权): * 1、如果用户正常退出,缓存自动清空; * 2、如果用户非正常退出,缓存自动清空; * 3、如果我们修改了用户的权限,而用户不退出系统,修改的权限无法立即生效。 * (需要手动编程进行实现;放在service进行调用) * 在权限修改后调用realm中的方法,realm已经由spring管理,所以从spring中获取realm实例, * 调用clearCached方法; * :Authorization 是授权访问控制,用于对用户进行的操作授权,证明该用户是否允许进行当前操作,如访问某个链接,某个资源文件等。 * @param principalCollection * @return */ @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { /* * 当没有使用缓存的时候,不断刷新页面的话,这个代码会不断执行, * 当其实没有必要每次都重新设置权限信息,所以我们需要放到缓存中进行管理; * 当放到缓存中时,这样的话,doGetAuthorizationInfo就只会执行一次了, * 缓存过期之后会再次执行。 */ System.out.println("权限配置-----》MyShiroRealm.doGetAuthorizationInfo()"); SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo(); UserInfo userInfo = (UserInfo) principalCollection.getPrimaryPrincipal(); //实际项目中,这里可以根据实际情况做缓存,如果不做,Shiro自己也是有时间间隔机制,2分钟内不会重复执行该方法 // UserInfo userInfo = userInfoService.findByUsername(username) //权限单个添加; // 或者按下面这样添加 //添加一个角色,不是配置意义上的添加,而是证明该用户拥有admin角色 // authorizationInfo.addRole("admin"); //添加权限 // authorizationInfo.addStringPermission("userInfo:query"); //在认证成功之后返回. //设置角色信息. //支持 Set集合, //用户的角色对应的所有权限,如果只使用角色定义访问权限,下面的四行可以不要 // List<Role> roleList=user.getRoleList(); // for (Role role : roleList) { // info.addStringPermissions(role.getPermissionsName()); // } for (SysRole role: userInfo.getRoleList()) { simpleAuthorizationInfo.addRole(role.getRole()); for (SysPermission p: role.getPermissions()) { simpleAuthorizationInfo.addStringPermission(p.getPermission()); System.out.println("user拥有权限:" + p.getPermission()); } } return simpleAuthorizationInfo; } }
实现了自定义Realm之后,就需要把shiro配置到spring中:
package com.slf.firstappdemo.framework.shiro.config; import com.slf.firstappdemo.framework.shiro.realm.MyShiroRealm; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.mgt.SecurityManager; 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.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import java.util.LinkedHashMap; import java.util.Map; /** * @version v.0.1 * @Author:Flm * @Description:shiro config * @Date:16:15 2018/3/13 * Shiro 配置 * * Apache Shiro 核心通过 Filter 来实现,就好像SpringMvc 通过DispachServlet 来主控制一样。 * 既然是使用 Filter 一般也就能猜到,是通过URL规则来进行过滤和权限校验,所以我们需要定义一系列关于URL的规则和访问权限。 */ @Configuration public class ShiroConfig { /** * 凭证匹配器 * (由于我们的密码校验交给Shiro的SimpleAuthenticationInfo进行处理了 * 所以我们需要修改下doGetAuthenticationInfo中的代码; * ) * * @return */ @Bean public HashedCredentialsMatcher hashedCredentialsMatcher() { HashedCredentialsMatcher hashedCredentialsMatcher = new HashedCredentialsMatcher(); hashedCredentialsMatcher.setHashAlgorithmName("md5"); //散列算法:这里使用MD5算法; hashedCredentialsMatcher.setHashIterations(2); //散列的次数,比如散列两次,相当于 md5(md5("")); return hashedCredentialsMatcher; } /** * 身份认证realm; * (这个需要自己写,账号密码校验;权限等) * * @return */ @Bean public MyShiroRealm myShiroRealm() { MyShiroRealm myShiroRealm = new MyShiroRealm(); myShiroRealm.setCredentialsMatcher(hashedCredentialsMatcher()); return myShiroRealm; } @Bean public SecurityManager securityManager() { DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager(); //设置realm securityManager.setRealm(myShiroRealm()); return securityManager; } /** * ShiroFilterFactoryBean 处理拦截资源文件问题。 * 注意:单独一个ShiroFilterFactoryBean配置是或报错的,因为在 * 初始化ShiroFilterFactoryBean的时候需要注入:SecurityManager * <p> * Filter Chain定义说明 * 1、一个URL可以配置多个Filter,使用逗号分隔 * 2、当设置多个过滤器时,全部验证通过,才视为通过 * 3、部分过滤器可指定参数,如perms,roles */ @Bean public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) { System.out.println("ShiroConfiguration.shiroFilter()"); ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean(); //必须设置securityManager shiroFilterFactoryBean.setSecurityManager(securityManager); //拦截器 Map < String, String > filterChainDefinitioinMap = new LinkedHashMap < String, String > (); //配置退出过滤器,其中的具体的退出代码shiro已经实现 filterChainDefinitioinMap.put("/logout", "logout"); //过滤连定义,从上向下顺序执行,一般将/**放在最为下边 !!!!! //authc:所有的url都必须认证通过才可以访问;anon:所有的url都可以匿名访问 filterChainDefinitioinMap.put("/shiro/**", "authc"); //如果不设置默认会自动寻找web工程根目录下的"/login.jsp"页面 shiroFilterFactoryBean.setLoginUrl("/shiro/login"); //此处应是url并非静态资源位置 //登陆成功后要跳转的链接 shiroFilterFactoryBean.setSuccessUrl("/shiro/index"); //未授权页面 shiroFilterFactoryBean.setUnauthorizedUrl("/403"); shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitioinMap); return shiroFilterFactoryBean; } /** * 开启shiro aop注解支持. * 使用代理方式;所以需要开启代码支持; * * @param securityManager * @return */ @Bean public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) { AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor(); authorizationAttributeSourceAdvisor.setSecurityManager(securityManager); return authorizationAttributeSourceAdvisor; } }
这样我们就全部配置成功可以尽情的使用它了。
以上就是本篇文章的所有内容,更多java项目中常见问题及解决方法,可以关注我们网站了解具体。
推荐阅读: