废话少说,先简单说下怎么用,至于shiro是什么,请移步shiro官网
1、添加依赖
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-core</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-web</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
<dependency>
<groupId>net.mingsoft</groupId>
<artifactId>shiro-freemarker-tags</artifactId>
<version>${shiro-freemarker.version}</version>
</dependency>
2、重写AuthorizingRealm
AuthorizingRealm主要包括两个方法:doGetAuthorizationInfo(PrincipalCollection principals)和doGetAuthenticationInfo(AuthenticationToken token)
1.doGetAuthenticationInfo(AuthenticationToken token)在登录认证时候被调用,即SecurityUtils.getSubject().login()执行时候被调用
2.doGetAuthorizationInfo(PrincipalCollection principals)获取权限认证信息,即SecurityUtils.getSubject().isPermitted()执行时候被调用
我这里定义成abstract 是为了提取该类为公用,其他项目可以复用集成。
public abstract class AbstarctAuthorizingRealm extends AuthorizingRealm {
@Resource
private IUserService userService;
@Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
Object primaryPrincipal = principals.getPrimaryPrincipal();
if (primaryPrincipal == null) {
return null;
}
IUser user = (IUser) primaryPrincipal;
SimpleAuthorizationInfo info = new SimpleAuthorizationInfo();
info.setStringPermissions(findPermissions(user));
info.setRoles(findRoles(user));
return info;
}
/**
* 获取用户的权限集合
*/
protected abstract Set<String> findPermissions(IUser user);
/**
* 获取用户的角色集合
*/
protected abstract Set<String> findRoles(IUser user);
@Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
UsernamePasswordToken upt = (UsernamePasswordToken) token;
String username = upt.getUsername();
char[] password = upt.getPassword();
if (StringUtils.isBlank(username)) {
throw new MustUsernameException();
}
if (password == null || password.length == 0) {
throw new MustPasswordException();
}
IUser user = userService.findUserByUsername(username);
if (user == null) {
throw new UnknownAccountException();
}
if (Boolean.TRUE.equals(user.getLocked())) {
throw new LockedAccountException();
}
if (Boolean.TRUE.equals(user.getDisabled())) {
throw new DisabledAccountException();
}
AuthenticationInfo authenticationInfo = assertAuthenticationInfo(user);
return authenticationInfo;
}
protected AuthenticationInfo assertAuthenticationInfo(IUser user) {
SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo(user, user.getPassword(), getName());
return authenticationInfo;
}
protected void assertCredentialsMatch(AuthenticationToken token, AuthenticationInfo info)
throws AuthenticationException {
CredentialsMatcher cm = getCredentialsMatcher();
if (cm != null) {
if (!cm.doCredentialsMatch(token, info)) {
throw new IncorrectCredentialsException();
}
} else {
throw new AuthenticationException("A CredentialsMatcher must be configured in order to verify "
+ "credentials during authentication. If you do not wish for credentials to be examined, you "
+ "can configure an " + AllowAllCredentialsMatcher.class.getName() + " instance.");
}
}
public IUserService getUserService() {
return userService;
}
public void setUserService(IUserService userService) {
this.userService = userService;
}
IUser也是一个接口,包括用户基本登录信息,方便每个用到的项目自己做实现
public interface IUser {
String getUsername();
String getPassword();
Boolean getDisabled();
Integer getDeleted();
Boolean getLocked();
}
3、登录成功回调(重写FormAuthenticationFilter)
比如登录成功后需要修改用户最后登录时间,登录ip,记录日志等等操作都可以在这里进行。
public class KaptchaFormAuthenticationFilter extends FormAuthenticationFilter {
final Logger logger = LoggerFactory.getLogger(getClass());
private MessageSourceAccessor messageSourceAccessor;
public KaptchaFormAuthenticationFilter(MessageSourceAccessor messageSourceAccessor) {
this.messageSourceAccessor = messageSourceAccessor;
}
protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue)
throws Exception {
if (request.getAttribute(getFailureKeyAttribute()) != null) {
return true;
}
return super.onAccessDenied(request, response, mappedValue);
}
@Override
protected boolean onLoginSuccess(AuthenticationToken token, Subject subject, ServletRequest request,
ServletResponse response) throws Exception {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))) {
httpServletResponse.setCharacterEncoding("UTF-8");
PrintWriter out = null;
try{
out = httpServletResponse.getWriter();
out.println(FastJsonUtils.toJSONString(ResultModel.defaultSuccess(null)));
out.flush();
}finally{
if(out != null){
out.close();
}
}
return false;
}
return super.onLoginSuccess(token, subject, request, response);
}
@Override
protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request,
ServletResponse response) {
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
if ("XMLHttpRequest".equalsIgnoreCase(httpServletRequest.getHeader("X-Requested-With"))) {
String className = e.getClass().getName();
ResultModel rm = new ResultModel(ResultStatus.FAIL.getCode(), messageSourceAccessor.getMessage(className));
PrintWriter out = null;
try {
out = httpServletResponse.getWriter();
out.println(FastJsonUtils.toJSONString(rm));
out.flush();
} catch (IOException e1) {
if(logger.isInfoEnabled()){
e1.printStackTrace();
}
}finally{
if(out != null){
out.close();
}
}
return false;
}
return super.onLoginFailure(token, e, request, response);
}
public MessageSourceAccessor getMessageSourceAccessor() {
return messageSourceAccessor;
}
public void setMessageSourceAccessor(MessageSourceAccessor messageSourceAccessor) {
this.messageSourceAccessor = messageSourceAccessor;
}
4、springboot配置shiroConfig
package com.mos.easyboot.admin.config;
import com.google.common.collect.Maps;
import com.mos.easyboot.admin.config.service.impl.PermissionService;
import com.mos.easyboot.admin.config.service.impl.UserService;
import com.mos.easyboot.tools.shiro.KaptchaValidateFilter;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
import org.apache.shiro.web.filter.authc.LogoutFilter;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.MethodInvokingFactoryBean;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.support.MessageSourceAccessor;
import javax.servlet.Filter;
import java.util.Map;
/**
* @author 小尘哥
*/
@Configuration
public class ShiroConfig{
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(PermissionService permissionService) {
DefaultWebSecurityManager manager = new DefaultWebSecurityManager();
manager.setRealm(userRealm(permissionService));
return manager;
}
@Bean
public MethodInvokingFactoryBean setSecurityManager(PermissionService permissionService) {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("org.apache.shiro.SecurityUtils.setSecurityManager");
methodInvokingFactoryBean.setArguments(securityManager(permissionService));
return methodInvokingFactoryBean;
}
@Bean
@DependsOn(value = "lifecycleBeanPostProcessor")
public UserRealm userRealm(PermissionService permissionService) {
UserRealm userRealm = new UserRealm(permissionService);
userRealm.setCredentialsMatcher(credentialsMatcher());
return userRealm;
}
@Bean
public HashedCredentialsMatcher credentialsMatcher() {
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName("MD5");
// credentialsMatcher.setHashIterations(2); MD5加密迭代次数1:MD5(str),2:MD5(MD5(str))
return credentialsMatcher;
}
@Bean
public FormAuthenticationFilter authcFilter(@Qualifier("messageSourceAccessor") MessageSourceAccessor messageSourceAccessor,
@Qualifier("userService") UserService userService) {
CustomFormAuthenticationFilter authenticationFilter = new CustomFormAuthenticationFilter(messageSourceAccessor,userService);
authenticationFilter.setLoginUrl("/login");
return authenticationFilter;
}
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(
@Qualifier("permissionService") PermissionService permissionService) {
AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
authorizationAttributeSourceAdvisor.setSecurityManager(securityManager(permissionService));
return authorizationAttributeSourceAdvisor;
}
public LogoutFilter logoutFilter() {
LogoutFilter logout = new LogoutFilter();
logout.setRedirectUrl("/login");
return logout;
}
@Bean
public KaptchaValidateFilter kaptchaValidate() {
return new KaptchaValidateFilter();
}
@Bean(name = "shiroFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("messageSourceAccessor") MessageSourceAccessor messageSourceAccessor,
@Qualifier("userService") UserService userService,
@Qualifier("permissionService") PermissionService permissionService) {
ShiroFilterFactoryBean bean = new ShiroFilterFactoryBean();
bean.setSecurityManager(securityManager(permissionService));
bean.setLoginUrl("/toLogin");
bean.setUnauthorizedUrl("/unauthor");
bean.setSuccessUrl("/index");
Map<String, Filter> filters = Maps.newHashMap();
filters.put("kaptchaValidate", kaptchaValidate());
filters.put("authc", authcFilter(messageSourceAccessor,userService));
filters.put("logout", logoutFilter());
bean.setFilters(filters);
Map<String, String> filterChainDefinitionMap = Maps.newHashMap();
filterChainDefinitionMap.put("/toLogin", "anon");
filterChainDefinitionMap.put("/login*", "kaptchaValidate,authc");
filterChainDefinitionMap.put("/logout", "logout");
filterChainDefinitionMap.put("/demo/**", "anon");
filterChainDefinitionMap.put("/kaptcha.jpg", "anon");
filterChainDefinitionMap.put("/app/**", "anon");
filterChainDefinitionMap.put("/images/**", "anon");
filterChainDefinitionMap.put("/css/**", "anon");
filterChainDefinitionMap.put("/js/**", "anon");
filterChainDefinitionMap.put("/plugins/**", "anon");
filterChainDefinitionMap.put("/**", "user");
bean.setFilterChainDefinitionMap(filterChainDefinitionMap);
return bean;
}
@Bean
public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
return new LifecycleBeanPostProcessor();
}
@Bean
public MessageSourceAccessor messageSourceAccessor(@Qualifier("messageSource") MessageSource messageSource){
return new MessageSourceAccessor(messageSource);
}
}
5、添加shiro拦截
@Bean
public FilterRegistrationBean shiroFilterRegistration() {
FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistration.setEnabled(true);
filterRegistration.addUrlPatterns("/*");
filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
return filterRegistration;
}
6、使用
controller直接加@RequiresPermissions("对应的权限")或@RequiresRoles("对应的角色")即可
模板上:<@shiro.hasPermission name="你定义的权限标识">/@shiro.hasPermission
7、其他
有些代码没有贴出来,随后我会把整个项目开源出来,easy-boot,基于springboot的快速开发框架搭建方案,希望大家多多支(tu)持(cao)····