按照上篇的cas服务端的配置一样,集成shiro+pac4j,其实在1.3还是1.2shiro-cas就过期了,shiro官方也让我们集成pac4j,但是居然居然没有demo,我也是服,,。。
此篇博客将集成springboot+cas5+shiro+pac4j 集成在一起,是宝宝将近一个月的心血,还有老大的帮忙!!!!
开始我的表演:
首先是pom
4.0.0
com.bofeng.shiro
shiro-cas-pac4j
1.0.0
1.8
UTF-8
1.8
1.1.3
3.4
18.0
1.2.8
2.4
1.10
1.4.0
3.0.0
2.1.0
3.4.1
0.2.7
1.7.21
1.1.7
1.16.20
org.apache.shiro
shiro-spring-boot-web-starter
${shiro.version}
io.buji
buji-pac4j
${buji.version}
org.pac4j
pac4j-cas
${pac4j.version}
org.pac4j
pac4j-jwt
${pac4j.version}
org.pac4j
pac4j-http
${pac4j.version}
com.alibaba
fastjson
1.2.7
org.slf4j
slf4j-api
${slf4j.version}
ch.qos.logback
logback-classic
${logback.version}
ch.qos.logback
logback-core
${logback.version}
org.projectlombok
lombok
${lombok.version}
provided
org.springframework.boot
spring-boot-maven-plugin
true
然后是shiro 的认证,由于集成了pac4j,所以我们只要关心权限
的控制就可以了
@Slf4j
public class ShiroPac4jRealm extends Pac4jRealm {
@Override
protected AuthorizationInfo doGetAuthorizationInfo(final PrincipalCollection principals) {
Object user = super.getAvailablePrincipal(principals);
log.info("登录用户:{}", user);
SimpleAuthorizationInfo simpleAuthorizationInfo = new SimpleAuthorizationInfo();
List permissions = new ArrayList();
permissions.add("user:info");
simpleAuthorizationInfo.addStringPermissions(permissions);
return simpleAuthorizationInfo;
}
}
shiro这里不详解,只说集成,shiro的配置如下
package com.bofeng.shiro.config;
import io.buji.pac4j.filter.CallbackFilter;
import io.buji.pac4j.filter.SecurityFilter;
import io.buji.pac4j.subject.Pac4jSubjectFactory;
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.jasig.cas.client.session.SingleSignOutFilter;
import org.jasig.cas.client.session.SingleSignOutHttpSessionListener;
import org.pac4j.cas.client.CasClient;
import org.pac4j.cas.client.rest.CasRestFormClient;
import org.pac4j.cas.config.CasConfiguration;
import org.pac4j.cas.config.CasProtocol;
import org.pac4j.core.client.Clients;
import org.pac4j.core.config.Config;
import org.pac4j.core.matching.PathMatcher;
import org.pac4j.http.client.direct.ParameterClient;
import org.pac4j.jwt.config.encryption.SecretEncryptionConfiguration;
import org.pac4j.jwt.config.signature.SecretSignatureConfiguration;
import org.pac4j.jwt.credentials.authenticator.JwtAuthenticator;
import org.pac4j.jwt.profile.JwtGenerator;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletListenerRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.web.filter.DelegatingFilterProxy;
import javax.servlet.Filter;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration extends AbstractShiroWebFilterConfiguration {
@Value("#{ @environment['cas.prefixUrl'] ?: null }")
private String prefixUrl;
@Value("#{ @environment['cas.loginUrl'] ?: null }")
private String casLoginUrl;
@Value("#{ @environment['cas.callbackUrl'] ?: null }")
private String callbackUrl;
//jwt秘钥
@Value("${jwt.salt}")
private String salt;
/**
* JWT Token 生成器,对CommonProfile生成然后每次携带token访问
*
* @return
*/
@SuppressWarnings("rawtypes")
@Bean
protected JwtGenerator jwtGenerator() {
return new JwtGenerator(new SecretSignatureConfiguration(salt), new SecretEncryptionConfiguration(salt));
}
@Bean
protected JwtAuthenticator jwtAuthenticator() {
JwtAuthenticator jwtAuthenticator = new JwtAuthenticator();
jwtAuthenticator.addSignatureConfiguration(new SecretSignatureConfiguration(salt));
jwtAuthenticator.addEncryptionConfiguration(new SecretEncryptionConfiguration(salt));
return jwtAuthenticator;
}
/**
* cas的基本设置,包括或url等等,rest调用协议等
*
* @return
*/
@Bean
public CasConfiguration casConfiguration() {
CasConfiguration casConfiguration = new CasConfiguration(casLoginUrl);
casConfiguration.setProtocol(CasProtocol.CAS20);
casConfiguration.setPrefixUrl(prefixUrl);
return casConfiguration;
}
/**
* 不拦截的路径
*
* @return
*/
@Bean
public PathMatcher pathMatcher() {
PathMatcher pathMatcher = new PathMatcher();
pathMatcher.excludePath("/html/**");
return pathMatcher;
}
/**
* pac4jRealm
*
* @return
*/
@Bean(name = "pac4jRealm")
public Realm pac4jRealm() {
return new ShiroPac4jRealm();
}
/**
* 通过rest接口可以获取tgt,获取service ticket,甚至可以获取CasProfile
*
* @return
*/
@Bean
protected CasRestFormClient casRestFormClient(CasConfiguration casConfiguration) {
CasRestFormClient casRestFormClient = new CasRestFormClient();
casRestFormClient.setConfiguration(casConfiguration);
casRestFormClient.setName("rest");
return casRestFormClient;
}
/**
* casClient
*
* @return
*/
@Bean
public CasClient casClient(CasConfiguration casConfiguration) {
CasClient casClient = new CasClient();
casClient.setConfiguration(casConfiguration);
casClient.setCallbackUrl(callbackUrl);
casClient.setName("cas");
return casClient;
}
/**
* token校验相关
*
* @return
*/
@Bean
protected Clients clients(CasClient casClient, CasRestFormClient casRestFormClient) {
//可以设置默认client
Clients clients = new Clients();
//token校验器,可以用HeaderClient更安全
ParameterClient parameterClient = new ParameterClient("token", jwtAuthenticator());
parameterClient.setSupportGetRequest(true);
parameterClient.setName("jwt");
//支持的client全部设置进去
clients.setClients(casClient, casRestFormClient, parameterClient);
return clients;
}
@Bean
protected Config casConfig(Clients clients) {
Config config = new Config();
config.setClients(clients);
return config;
}
/**
* 由于cas代理了用户,所以必须通过cas进行创建对象
*
* @return
*/
@Bean(name = "subjectFactory")
protected SubjectFactory subjectFactory() {
return new Pac4jSubjectFactory();
}
/**
* 单点登出的listener
*
* @return
*/
@SuppressWarnings({"rawtypes", "unchecked"})
@Bean
public ServletListenerRegistrationBean> singleSignOutHttpSessionListener() {
ServletListenerRegistrationBean bean = new ServletListenerRegistrationBean();
bean.setListener(new SingleSignOutHttpSessionListener());
bean.setEnabled(true);
return bean;
}
/**
* 单点登出filter
*
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public FilterRegistrationBean singleSignOutFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setName("singleSignOutFilter");
SingleSignOutFilter singleSignOutFilter = new SingleSignOutFilter();
singleSignOutFilter.setCasServerUrlPrefix(prefixUrl);
singleSignOutFilter.setIgnoreInitConfiguration(true);
bean.setFilter(singleSignOutFilter);
bean.addUrlPatterns("/*");
bean.setEnabled(true);
return bean;
}
@Bean(name = "securityManager")
public DefaultWebSecurityManager securityManager(Realm pac4jRealm, SubjectFactory subjectFactory) {
DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
defaultWebSecurityManager.setRealm(pac4jRealm);
defaultWebSecurityManager.setSubjectFactory(subjectFactory);
return defaultWebSecurityManager;
}
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new DelegatingFilterProxy("shiroFilter"));
filterRegistrationBean.addInitParameter("targetFilterLifecycle", "true");
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*");
return filterRegistrationBean;
}
@Bean(name = "shiroFilter")
protected ShiroFilterFactoryBean shiroFilterFactoryBean(DefaultWebSecurityManager securityManager, Config config) {
ShiroFilterFactoryBean filterFactoryBean = super.shiroFilterFactoryBean();
filterFactoryBean.setSecurityManager(securityManager);
//过滤器设置
Map filters = new HashMap();
SecurityFilter securityFilter = new SecurityFilter();
securityFilter.setClients("cas,rest,jwt");
securityFilter.setConfig(config);
filters.put("casSecurityFilter", securityFilter);
CallbackFilter callbackFilter = new CallbackFilter();
callbackFilter.setConfig(config);
filters.put("callbackFilter", callbackFilter);
filterFactoryBean.setFilters(filters);
return filterFactoryBean;
}
@Bean
public ShiroFilterChainDefinition shiroFilterChainDefinition() {
DefaultShiroFilterChainDefinition definition = new DefaultShiroFilterChainDefinition();
definition.addPathDefinition("/callback", "callbackFilter");
definition.addPathDefinition("/you", "anon");
definition.addPathDefinition("/**", "casSecurityFilter");
return definition;
}
/**
* 开启Shiro的注解(如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,并在必要时进行安全逻辑验证
* 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)和AuthorizationAttributeSourceAdvisor)即可实现此功能
*
* @return
*/
@Bean
@DependsOn({"lifecycleBeanPostProcessor"})
public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
advisorAutoProxyCreator.setProxyTargetClass(true);
return advisorAutoProxyCreator;
}
/**
* 开启 shiro aop注解支持
*
* @param securityManager
* @return
*/
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(DefaultWebSecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor aasa = new AuthorizationAttributeSourceAdvisor();
aasa.setSecurityManager(securityManager);
return aasa;
}
}
一个配置类
package com.bofeng.shiro.config;
import org.apache.shiro.authz.AuthorizationException;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
@RestControllerAdvice
public class ShiroExceptionHandler {
// @ExceptionHandler(value = {AuthorizationException.class})
// public Map authorizationExceptionHandler(HttpServletRequest request, Exception e) {
// return noPermissionResult();
// }
@ExceptionHandler(value = {UnauthorizedException.class})
public Map unauthorizedExceptionHandler(HttpServletRequest request, Exception e) {
return noPermissionResult();
}
private Map noPermissionResult() {
Map result = new HashMap() {
private static final long serialVersionUID = 1L;
{
put("errcode", 0);
put("errmsg", "暂无权限");
}
};
return result;
}
}
启动类顺便也贴出来了
package com.bofeng.shiro;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.annotation.RequiresPermissions;
import org.apache.shiro.subject.PrincipalCollection;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@RequiresPermissions(value = "role:edit")
@GetMapping(value = "/roles/{id}")
public String put(){
return "允许修改角色";
}
@RequiresPermissions(value = "user:info")
@GetMapping(value = "/users/{id}")
public PrincipalCollection getUserById() {
return SecurityUtils.getSubject().getPrincipals();
}
@GetMapping(value = "/you")
public String you(){
return "you you 不需要权限";
}
}
最重要的yml文件
server:
port: 8083
spring:
http:
encoding:
charset: UTF-8
enabled: true
force: true
mail:
default-encoding: UTF-8
messages:
encoding: UTF-8
casServerUrlPrefix: https://passport.sso.com:9898/cas
cas:
prefixUrl: https://passport.sso.com:9898/cas
loginUrl: ${cas.prefixUrl}/login
logoutUrl: ${cas.prefixUrl}/logout
serviceUrl: http://www.casclient2.com:${server.port}
callbackUrl: ${cas.serviceUrl}/callback
jwt:
salt: cas
debug: true
配置什么的你们看自己需求改,host别说不会
到这里Springboot的客户端就集成好了,如果还是有问题的话Q845896876交流
如果这篇博客帮你解决了问题,那么那么请给宝宝一点动力
源码提供给大家参考 请分开tomcat部署 https://download.csdn.net/download/weixin_39819191/10422417
验证码,自定义登录页面>https://blog.csdn.net/u010588262/article/details/80014083
这个是前后端分离AJAX调用时返回的类,但是发现个没效果的问题,
最后让前端在请求头里面加入 X-Requested-With=XMLHttpRequest 就好了,不知道怎么回事,,,
判断是不是ajax的类
问题: ajax请求后无法正常登陆,确认是session不对,跨域问题,
解决:前端判断401,跳转到后台一个特殊接口,接口无权访问会跳到cas登录页,登陆后跳转到接口,接口转发到前端路由
有点绕,,,但是就这么解决问题了,,