spring security验证流程

2023-05-16

工作需要,又弄起了权限的管理。虽然很早以前都了解过基于容器的权限实现方式,但是一直都觉得那东西太简陋了。后来使用liferay时发现它的权限系统的确做得很优秀,感觉这也可能是它做得最出色的地方吧。但是当时只停留在怎么使用及一些与其衔接的关系之上,并没有对其底层进行了解,新到现在的公司后,发现这一课还是得补上。但是令人惊讶的是,目前可用的选择并不多,甚至很少,最有名的当属spring security了,虽然很有名,但是关于这方面的资料并不是很多,应用示例就更少了。还好有中文的官方文档与http://www.family168.com/bbs/发布的简要教程,因此学起来不至于太困难。然后参照了一篇downpour写的spring security文章,因此勉强熟悉了spring security的应用开发,但是基本只停留在勉强会用的基础之上,而且还花了本人不少时间,在一个项目的运用中,自己更是差点没下得了台,惊出了一身冷汗。当时的感觉spring security就是个垃圾东西,运用很复杂,哪怕是做一个只拦截路径的权限系统,也要经过很多步骤。现在熟悉了它的一些流程后,虽然不知道这样的实现方式是否是最合理的,但是的确也有它的道理。现在利用放假期间,可以静下心来,理解一些以前让自己迷惑的东西了。downpour牛人的那篇文章讲得很好,以至于本人着实花了点时间才把它完全熟悉,当前自己以前对acegi并不熟悉。熟悉了那篇文章后,还是有些地方让自己不太理解,其中之一就是spring security是怎样完成用户角色权限验证的。下面就对这人问题进行简单的介绍:

首先这篇文章是基于downpour那篇文章的,其地址为:
[url]http://www.iteye.com/topic/319965[/url]

最先着手就是配置文件,这也是整个spring security最重要的入口点:


............
<!-- 处理国际化信息 -->
<beans:bean id="authenticationManager"
class="org.springframework.security.providers.ProviderManager">
<beans:property name="messageSource" ref="messageSource" />
</beans:bean>

<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename"
value="classpath:org/springframework/security/messages_zh_CN" />
</beans:bean>

<authentication-provider user-service-ref="securityManager">
<password-encoder hash="md5" />
</authentication-provider>

<!-- AffirmativeBased表示只要有一个Voter通过权限要求,就可以访问 -->
<beans:bean id="accessDecisionManager"
class="org.springframework.security.vote.AffirmativeBased">
<!-- 是否允许所有的投票者弃权,如果为false,表示如果所有的投票者弃权,就禁止访问 -->
<beans:property name="allowIfAllAbstainDecisions"
value="false" />
<beans:property name="decisionVoters">
<beans:list>
<!-- RoleVoter默认角色名称都要以ROLE_开头,否则不会被计入权限控制,如果要修改前缀,可以通过对rolePrefix属性进行修改 -->
<beans:bean class="org.springframework.security.vote.RoleVoter" />
<beans:bean class="org.springframework.security.vote.AuthenticatedVoter" />
</beans:list>
</beans:property>
</beans:bean>

<beans:bean id="resourceSecurityInterceptor"
class="org.springframework.security.intercept.web.FilterSecurityInterceptor">
<beans:property name="authenticationManager" ref="authenticationManager" />
<beans:property name="accessDecisionManager" ref="accessDecisionManager" />
<beans:property name="objectDefinitionSource"
ref="secureResourceFilterInvocationDefinitionSource" />
<!-- 每次请求都进行检查,如果设为true,则只第一次检查,默认为true -->
<beans:property name="observeOncePerRequest" value="false" />
<custom-filter after="LAST" />
</beans:bean>

<beans:bean id="secureResourceFilterInvocationDefinitionSource"
class="com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource" />
....


上面的“accessDecisionManager”就是切入点,首先需要说明的是,在验证用户是否能通过验证时,spring security提供了三种策略,分别对应那个策略类:
UnanimousBased.java 只要有一个Voter不能完全通过权限要求,就禁止访问。
AffirmativeBased.java只要有一个Voter可以通过权限要求,就可以访问。
ConsensusBased.java只要通过的Voter比禁止的Voter数目多就可以访问了。

在此说一点,ConsensusBased这个类有点特别,如果通过的票数与禁止的票数相同怎么办?
这个类有个allowIfEqualGrantedDeniedDecisions属性,默认为true,关键代码:


if ((grant == deny) && (grant != 0)) {
if (this.allowIfEqualGrantedDeniedDecisions) {
return;
} else {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}
}


上面的代码表示如果allowIfEqualGrantedDeniedDecisions为true而且通过的票数不为0就授权访问。

在提供好验证策略以后,继续关注其是怎么进行验证的。在上面的XML文件中,accessDescisionManager有个allowIfAllAbstainDecisions属性,这个属性的默认值为false,作用见注释处。现在主要关注的是其decisionVoters属性,中文的理解就是投票,RoleVoter.java 主要作用就是完成角色的投票验证,需要注意的是,它验证的角色,名称必须以ROLE_开头,当然这也可以通过配置文件改变,如:


<bean id="roleVoter" class="org.springframework.security.vote.RoleVoter">
<property name="rolePrefix" value="AUTH_"/>
</bean>


至于RoleVoter是如何完成验证的呆会再说,先回顾一下com.javaeye.sample.security.interceptor.SecureResourceFilterInvocationDefinitionSource:


.....
public ConfigAttributeDefinition getAttributes(Object filter) throws IllegalArgumentException {

FilterInvocation filterInvocation = (FilterInvocation) filter;
String requestURI = filterInvocation.getRequestUrl();
Map<String, String> urlAuthorities = this.getUrlAuthorities(filterInvocation);

String grantedAuthorities = null;
for(Iterator<Map.Entry<String, String>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
Map.Entry<String, String> entry = iter.next();

//url表示从资源表取出的值,在这里代表的是相应的URL
String url = entry.getKey();

//这段代码表示数据库内的需要验证的资源URL与当前请求的URL相匹配时进行验证
if(urlMatcher.pathMatchesUrl(url, requestURI)) {
//grantedAuthorities表示每个资源对应的角色,如果有多个角色,则以','隔开
grantedAuthorities = entry.getValue();
break;
}
}

if(grantedAuthorities != null) {
ConfigAttributeEditor configAttrEditor = new ConfigAttributeEditor();
configAttrEditor.setAsText(grantedAuthorities);
return (ConfigAttributeDefinition) configAttrEditor.getValue();
}
return null;
}
....


虽然不重要,但是还是有必要引用一下:

[quote]
处于继承树顶端的AbstractSecurityInterceptor有三个实现类:

FilterSecurityInterceptor,负责处理FilterInvocation,实现对URL资源的拦截。
MethodSecurityInterceptor,负责处理MethodInvocation,实现对方法调用的拦截。
AspectJSecurityInterceptor,负责处理JoinPoint,主要也是用于对方法调用的拦截。

为了限制用户访问被保护资源,Spring Security提供了一套元数据,用于定义被保护资源的访问权限,这套元数据主要体现为ConfigAttribute和ConfigAttributeDefinition。每个ConfigAttribute中只包含一个字符串,而一个ConfigAttributeDefinition中可以包含多个ConfigAttribute。对于系统来说,每个被保护资源都将对应一个ConfigAttributeDefinition,这个ConfigAttributeDefinition中包含的多个ConfigAttribute就是访问该资源所需的权限。

实际应用中,ConfigAttributeDefinition会保存在ObjectDefinitionSource中,这是一个主要接口,FilterSecurityInterceptor所需的DefaultFilterInvocationDefinitionSource和MethodSecurityInterceptor所需的MethodDefinitionAttributes都实现了这个接口。ObjectDefinitionSource可以看做是Spring Security中权限配置的源头,框架内部所有的验证组件都是从ObjectDefintionSource中获得数据,来对被保护资源进行权限控制的。

为了从xml中将用户配置的访问权限转换成ObjectDefinitionSource类型的对象,Spring Security专门扩展了Spring中提供的PropertyEditor实现了ConfigAttributeEditor,它可以把以逗号分隔的一系列字符串转换成包含多个ConfigAttribute的ConfigAttributeDefintion对象。

"ROLE_ADMIN,ROLE_USER"



ConfigAttributeDefinition
ConfigAttribute["ROLE_ADMIN"]
ConfigAttribute["ROLE_USER"]

对于FilterSecurityInterceptor来说,最终生成的就是一个包含了url pattern和ConfigAttributeConfiguration的ObjectDefinitionSource。


<intercept-url pattern="/admin.jsp" access="ROLE_ADMIN,ROLE_USER" />



ConfigAttributeDefinition
"/admin.jsp" → ConfigAttribute["ROLE_ADMIN"]
ConfigAttribute["ROLE_USER"]

换而言之,无论我们将权限配置的原始数据保存在什么地方,只要最终可以将其转换为ObjectDefintionSource就可以提供给验证组件进行调用,实现权限控制。

[/quote]

当时一直不明白这个getAttributes到底拿来做什么的。下面一步步进行追终,通过配置文件可知,这个类首先会到org.springframework.security.intercept.web.FilterSecurityInterceptor这个类中,这个类有个主要的方法:


public void invoke(FilterInvocation fi) throws IOException, ServletException {
if ((fi.getRequest() != null) && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
&& observeOncePerRequest) {
// filter already applied to this request and user wants us to observce
// once-per-request handling, so don't re-do security checking
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} else {
// first time this request being called, so perform security checking
if (fi.getRequest() != null) {
fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
}

InterceptorStatusToken token = super.beforeInvocation(fi);

try {
fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
} finally {
super.afterInvocation(token, null);
}
}
}


在此有两点需要说明,首先是observeOncePerRequest属性,一般情况下保持其默认的true,文档上有说:


/***
Indicates whether once-per-request handling will be observed. By default this is <code>true</code>,meaning the <code>FilterSecurityInterceptor</code> will only execute once-per-request. Sometimes users may wishit to execute more than once per request, such as when JSP forwards are being used and filter security is desired on each included fragment of the HTTP request.
*/


其次我们需要关注的重点是FilterSecurityInterceptor的超类AbstractSecurityInterceptor的beforeInvocation方法,下面贴出这个类中我们最需要关注的代码:


protected InterceptorStatusToken beforeInvocation(Object object) {
Assert.notNull(object, "Object was null");

if (!getSecureObjectClass().isAssignableFrom(object.getClass())) {
throw new IllegalArgumentException("Security invocation attempted for object "
+ object.getClass().getName()
+ " but AbstractSecurityInterceptor only configured to support secure objects of type: "
+ getSecureObjectClass());
}

ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);

if (attr == null) {
if (rejectPublicInvocations) {
throw new IllegalArgumentException(
"No public invocations are allowed via this AbstractSecurityInterceptor. "
+ "This indicates a configuration error because the "
+ "AbstractSecurityInterceptor.rejectPublicInvocations property is set to 'true'");
}

if (logger.isDebugEnabled()) {
logger.debug("Public object - authentication not attempted");
}

publishEvent(new PublicInvocationEvent(object));

return null; // no further work post-invocation
}

if (logger.isDebugEnabled()) {
logger.debug("Secure object: " + object + "; ConfigAttributes: " + attr);
}

if (SecurityContextHolder.getContext().getAuthentication() == null) {
credentialsNotFound(messages.getMessage("AbstractSecurityInterceptor.authenticationNotFound",
"An Authentication object was not found in the SecurityContext"), object, attr);
}

Authentication authenticated = authenticateIfRequired();

// Attempt authorization
try {
this.accessDecisionManager.decide(authenticated, object, attr);
}
catch (AccessDeniedException accessDeniedException) {
AuthorizationFailureEvent event = new AuthorizationFailureEvent(object, attr, authenticated,
accessDeniedException);
publishEvent(event);

throw accessDeniedException;
}

if (logger.isDebugEnabled()) {
logger.debug("Authorization successful");
}

AuthorizedEvent event = new AuthorizedEvent(object, attr, authenticated);
publishEvent(event);

// Attempt to run as a different user
Authentication runAs = this.runAsManager.buildRunAs(authenticated, object, attr);

if (runAs == null) {
if (logger.isDebugEnabled()) {
logger.debug("RunAsManager did not change Authentication object");
}

// no further work post-invocation
return new InterceptorStatusToken(authenticated, false, attr, object);
} else {
if (logger.isDebugEnabled()) {
logger.debug("Switching to RunAs Authentication: " + runAs);
}

SecurityContextHolder.getContext().setAuthentication(runAs);

// revert to token.Authenticated post-invocation
return new InterceptorStatusToken(authenticated, true, attr, object);
}
}

/**
* Checks the current authentication token and passes it to the AuthenticationManager if
* {@link org.springframework.security.Authentication#isAuthenticated()} returns false or the property
* <tt>alwaysReauthenticate</tt> has been set to true.
*
* @return an authenticated <tt>Authentication</tt> object.
*/
private Authentication authenticateIfRequired() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();

if (authentication.isAuthenticated() && !alwaysReauthenticate) {
if (logger.isDebugEnabled()) {
logger.debug("Previously Authenticated: " + authentication);
}

return authentication;
}


上面的代码首先关注其中的一行代码:


ConfigAttributeDefinition attr = this.obtainObjectDefinitionSource().getAttributes(object);


不错,这行代码就是SecureResourceFilterInvocationDefinitionSource存在的主要目的,它主要提供URL与ROLE这两个东西,至于需要验证的用户来源,上面有句代码:

Authentication authentication = SecurityContextHolder.getContext().getAuthentication();


众所周知,用户登陆成功后,可以通过:


(User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();


上面的代码可以获取当前登陆的用户的基本信息,所以验证时需要它也很自然的。既然所需要的东西都具备了,下面就讲讲该怎么验证的问题了。在上面的AbstractSecurityInterceptor内,有句代码:

this.accessDecisionManager.decide(authenticated, object, attr);


上面这个accessDecisionManager就是最开始讲的那个accessDecisionManager,终于回到原来的问题上了,由于上面的配置文件中使用了AffirmativeBased投票策略,下面就直接进入此类的decide方法:

public void decide(Authentication authentication, Object object, ConfigAttributeDefinition config)
throws AccessDeniedException {
Iterator iter = this.getDecisionVoters().iterator();
int deny = 0;

while (iter.hasNext()) {
AccessDecisionVoter voter = (AccessDecisionVoter) iter.next();
int result = voter.vote(authentication, object, config);

switch (result) {
case AccessDecisionVoter.ACCESS_GRANTED:
return;

case AccessDecisionVoter.ACCESS_DENIED:
deny++;

break;

default:
break;
}
}

if (deny > 0) {
throw new AccessDeniedException(messages.getMessage("AbstractAccessDecisionManager.accessDenied",
"Access is denied"));
}

// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();
}


这个方法主要有三个作用,第一作用就是完成投票,第二就是验证,从上面的switch与if语句可以看出,只要有一个投票者赞成,就直接返回,验证通过。如果没有一个投票者赞成(弃权)而且有人反对,deny++,到if(deny>0)时扔出异常,最后禁止访问。最后一句


//到这步时,所有的投票者都弃权了
// To get this far, every AccessDecisionVoter abstained
checkAllowIfAllAbstainDecisions();


这就到了allowIfAllAbstainDecisions属性起作用的时候了。下面就来讲讲AffirmativeBased中用到的投票类RoleVoter,这个类主要工作就是完成投票工作,然后将结果反馈给AffirmativeBased,下面列出RoleVoter的代码:


public class RoleVoter implements AccessDecisionVoter {
//~ Instance fields ================================================================================================

private String rolePrefix = "ROLE_";

//~ Methods ========================================================================================================

public String getRolePrefix() {
return rolePrefix;
}

/**
* Allows the default role prefix of <code>ROLE_</code> to be overridden.
* May be set to an empty value, although this is usually not desirable.
*
* @param rolePrefix the new prefix
*/
public void setRolePrefix(String rolePrefix) {
this.rolePrefix = rolePrefix;
}

public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null) && attribute.getAttribute().startsWith(getRolePrefix())) {
return true;
}
else {
return false;
}
}

/**
* This implementation supports any type of class, because it does not query
* the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class clazz) {
return true;
}

public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes().iterator();
GrantedAuthority[] authorities = extractAuthorities(authentication);

while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();

if (this.supports(attribute)) {
result = ACCESS_DENIED;

// Attempt to find a matching granted authority
for (int i = 0; i < authorities.length; i++) {
if (attribute.getAttribute().equals(authorities[i].getAuthority())) {
return ACCESS_GRANTED;
}
}
}
}

return result;
}

GrantedAuthority[] extractAuthorities(Authentication authentication) {
return authentication.getAuthorities();
}
}


这个类中最重要的代码就是这句:


if (attribute.getAttribute().equals(authorities[i].getAuthority())) {
return ACCESS_GRANTED;
}


这句代码的意思就是把SecureResourceFilterInvocationDefinitionSource传入的角色名称与SecurityContextHolder.getContext().getAuthentication()传入的用户所拥有的角色的角色名称相比较,如果相等则通过验证。

在配置文件中还用到了一个投票类AuthenticatedVoter,这个类与RoleVoter属于同级,RoleVoter用来验证角色,那AutherticatedVoter又是用来干什么的呢?
这个类完整代码:


public class AuthenticatedVoter implements AccessDecisionVoter {
//~ Static fields/initializers =====================================================================================

public static final String IS_AUTHENTICATED_FULLY = "IS_AUTHENTICATED_FULLY";
public static final String IS_AUTHENTICATED_REMEMBERED = "IS_AUTHENTICATED_REMEMBERED";
public static final String IS_AUTHENTICATED_ANONYMOUSLY = "IS_AUTHENTICATED_ANONYMOUSLY";
//~ Instance fields ================================================================================================

private AuthenticationTrustResolver authenticationTrustResolver = new AuthenticationTrustResolverImpl();

//~ Methods ========================================================================================================

private boolean isFullyAuthenticated(Authentication authentication) {
return (!authenticationTrustResolver.isAnonymous(authentication)
&& !authenticationTrustResolver.isRememberMe(authentication));
}

public void setAuthenticationTrustResolver(AuthenticationTrustResolver authenticationTrustResolver) {
Assert.notNull(authenticationTrustResolver, "AuthenticationTrustResolver cannot be set to null");
this.authenticationTrustResolver = authenticationTrustResolver;
}

public boolean supports(ConfigAttribute attribute) {
if ((attribute.getAttribute() != null)
&& (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())
|| IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())
|| IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute()))) {
return true;
} else {
return false;
}
}

/**
* This implementation supports any type of class, because it does not query the presented secure object.
*
* @param clazz the secure object
*
* @return always <code>true</code>
*/
public boolean supports(Class clazz) {
return true;
}

public int vote(Authentication authentication, Object object, ConfigAttributeDefinition config) {
int result = ACCESS_ABSTAIN;
Iterator iter = config.getConfigAttributes().iterator();

while (iter.hasNext()) {
ConfigAttribute attribute = (ConfigAttribute) iter.next();

if (this.supports(attribute)) {
result = ACCESS_DENIED;

if (IS_AUTHENTICATED_FULLY.equals(attribute.getAttribute())) {
if (isFullyAuthenticated(authentication)) {
return ACCESS_GRANTED;
}
}

if (IS_AUTHENTICATED_REMEMBERED.equals(attribute.getAttribute())) {
if (authenticationTrustResolver.isRememberMe(authentication)
|| isFullyAuthenticated(authentication)) {
return ACCESS_GRANTED;
}
}

if (IS_AUTHENTICATED_ANONYMOUSLY.equals(attribute.getAttribute())) {
if (authenticationTrustResolver.isAnonymous(authentication) || isFullyAuthenticated(authentication)
|| authenticationTrustResolver.isRememberMe(authentication)) {
return ACCESS_GRANTED;
}
}
}
}

return result;
}
}



作用见下面引用:
[quote]
AuthenticatedVoter用于判断ConfigAttribute上是否拥有IS_AUTHENTICATED_FULLY,IS_AUTHENTICATED_REMEMBERED或IS_AUTHENTICATED_ANONYMOUSLY之类的配置。
如果配置为IS_AUTHENTICATED_FULLY,那么只有AuthenticationTrustResolver的isAnonymous()和isRememberMe()都返回false时才能通过验证。
如果配置为IS_AUTHENTICATED_REMEMBERED,那么会在AuthenticationTrustResolver的isAnonymous()返回false时通过验证。
如果配置为IS_AUTHENTICATED_ANONYMOUSLY,就可以在AuthenticationTrustResolver的isAnonymous()和isRememberMe()两个方法返回任意值时都可以通过验证。
[/quote]

其中上面引用中的ConfigAttribute就是指SecureResourceFilterInvocationDefinitionSource的getAttributes()方法中ConfigAttributeDefinition中的ConfigAttribute,downpour的文章只是传入了ROLE_USER,ROLE_ADMIN等内容,要想让AuthenticatedVoter有用武之地,可以传入
IS_AUTHENTICATED_FULLY、IS_AUTHENTICATED_REMEMBERED、IS_AUTHENTICATED_ANONYMOUSLY中的值。

需要注意的是AffirmativeBased中遍历的投票者是要分先后的,也就是说RoleVoter在AuthenticatedVoter前面的话,会先进行RoleVoter验证,如果RoleVoter投票未通过,再进行了AuthenticatedVoter投票。

这样spring security的验证流程就基本清楚了,当然这篇文章也还是有些地方讲得不完善,以事有时间再来修改。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

spring security验证流程 的相关文章

  • 如何在url请求中发送数组

    我的要求如下 我想给出演员姓名 开始日期 结束日期并获取他在该时期出演的所有电影 因此 我的服务请求是这样的 http localhost 8080 MovieDB GetJson name Actor startDate 20120101
  • 如何在 iOS 上固定证书的公钥

    在提高我们正在开发的 iOS 应用程序的安全性时 我们发现需要对服务器的 SSL 证书 全部或部分 进行 PIN 操作以防止中间人攻击 尽管有多种方法可以做到这一点 但当您搜索此内容时 我只找到了固定整个证书的示例 这种做法会带来一个问题
  • 用于层次结构树角色的 Spring Security / Java EE 解决方案

    我知道 Spring Security 非常适合标准角色和基于权限的授权 我不确定的是这种情况 系统中管理着 10 000 名员工 员工被组织成组织结构图 跨部门的谁向谁报告的树 其中一些员工是用户 这些用户仅被允许访问其职责范围内的员工
  • 存储外部站点(不使用 OAuth)的用户凭据的智能方法是什么?

    我意识到 一般来说 您不应该直接存储用户凭据 即以纯文本形式 相反 最好存储它们的某种加密形式 但是 假设我创建了一个与其他第三方网站交互的网站 假设这个第 3 方站点提供了一个 API 需要用户的凭据 使用该站点 进行身份验证 如果我的目
  • 多线程Spring-boot控制器方法

    因此 我的应用程序 spring boot 运行速度非常慢 因为它使用 Selenium 来抓取数据 处理数据并显示在主页中 我遇到了多线程 我认为它对我的应用程序很有用 可以让它运行得更快 但是教程似乎显示在带有 main c 的普通 j
  • Spring Security 自定义过滤器

    我想自定义 Spring security 3 0 5 并将登录 URL 更改为 login 而不是 j spring security check 我需要做的是允许登录 目录并保护 admin report html 页面 首先 我使用教
  • Spring Boot - YML 配置 - 合并时擦除条目

    我的应用程序有一个基本 YML 配置 在类路径中如下所示 hello world values bar name bar name description bar description foo name foo name descript
  • 访问被拒绝(“java.io.FilePermission”“执行”)

    我是初学者 这是我写的第一个小程序 我想用小程序运行 exe 应用程序 java代码 package appletexample import java io import java awt import java applet Apple
  • 尝试使用JavaConfig在Spring中编写junit测试

    我正在尝试为我的示例项目编写一个 junit 测试 但不知道如何访问 jUnit 测试中的 ApplicationContext 这是该项目的主要类 public static void main String args in this s
  • 如何限制使用 Spring-Boot 和 OAuth2 登录特定域

    我已经使用 Spring Boot 和 Google 成功完成了 OAuth2 登录 但我想将登录限制到特定域 我们正在使用 Google Apps for Work 我认为我应该通过扩展类 OAuth2ClientAuthenticati
  • 限制对记录的访问。基于声明的权限是个好主意吗

    在 net 基于声明的身份框架中 如果我想限制用户对某个帐户 特定帐户 123456 执行操作 查看或编辑 我说的是商业实体 例如银行帐户 创建索赔是个好主意吗对于他们可以查看或编辑的每个帐户 一组中有很多索赔有什么缺点吗 系统管理员可能有
  • 防止 Spring Boot 注册 Spring Security 过滤器之一

    我想禁用安全链中的 Spring Security 过滤器之一 我已经看到了防止 Spring Boot 注册 servlet 过滤器 https stackoverflow com questions 28421966 prevent s
  • iPhone 和加密库

    我想我必须在我的 iPhone 应用程序中使用加密库 我想问你有关苹果公司实施的加密货币出口政策的影响 我需要做一些额外的事情吗 例如填写表格等 1 如果我使用 MD5 进行哈希处理 2 如果我使用对称加密 Thanks EDIT 2009
  • java.lang.ClassNotFoundException:com.fasterxml.jackson.core.JsonProcessingException

    我在用着templateRest发布User反对Rest Server但我遇到了以下错误 线程 main java lang NoClassDefFoundError 中出现异常 com fasterxml jackson core Jso
  • 春季 CORS。在允许的来源中添加模式

    查看CORS的弹簧指南 以下代码启用所有允许的来源 public class MyWebMVCConfigurer extends WebMvcConfigurerAdapter Override public void addCorsMa
  • 如何在 Spring 环境中更改属性?

    我在应用程序中使用 spring 环境 Bean 来获取应用程序配置属性 我想在不重新启动应用程序服务器的情况下从java代码更改spring环境中的属性值 我怎样才能做到这一点 Service public void MyService
  • 日志锻造强化修复

    我正在使用 Fortify SCA 来查找我的应用程序中的安全问题 作为大学作业 我遇到了一些无法解决的 日志锻造 问题 基本上 我记录一些来自 Web 界面的用户输入的值 logger warn current id not valid
  • 具有基于 java 的配置的 Spring Batch 的可跳过异常类

    我在 XML 中配置一个步骤 如下所示
  • 唯一索引或主键违规:“PRIMARY KEY ON PUBLIC.xxx”; SQL语句

    每当我的应用程序启动时 我都会收到以下错误消息 Caused by org h2 jdbc JdbcSQLException Unique index or primary key violation PRIMARY KEY ON PUBL
  • Spring引导@Transactional

    spring boot会自动在controller层添加 Transactional注解吗 我尝试将 Transactional 放在服务层 但似乎控制器层覆盖了注释 我有这个配置

随机推荐

  • 北京超级云计算中心操作训练指南

    北京超级云计算中心操作指南 本人在实验室做深度学习图像领域相关研究 xff0c 前期使用实验室的设备 2080Ti xff0c 运行时间较慢 xff1b 跑一轮需要6个小时以上 xff1b 后来开始使用超算 xff0c 运行速度比实验室快多
  • windows to go 和 linux to go 制作教程

    文章目录 使用 ventoy 制作windows to go 和 linux to go 教程 xff0c 将系统装进U盘中随身携带1 ventoy 介绍2 准备工作3 windows to go3 1 将 U盘初始化3 2 虚拟磁盘安装
  • 使用nps搭建内网穿透并配置泛域名解析

    使用nps搭建内网穿透并配置泛域名解析 前言1 准备工作2 服务器端搭建nps并配置2 1 配置nps配置文件2 2 docker安装nps2 3 web端配置nps并使用 3 客户端使用nps4 配置泛域名解析5 参考链接 前言 nps是
  • web内外网判断界面

    因日常需要 xff0c 我们在实验室内网中部署了一个服务 xff0c 在校园网内都能正常访问 xff0c 同时配置了内网穿透服务 xff0c 实现外网也能正常访问 但外网访问毕竟是通过内网穿透实现 xff0c 稳定性与网速都有限制 xff0
  • 为无登陆鉴权功能的接口与网站添加登陆鉴权功能

    1 缘由 本人部分服务的测试接口为方便日常测试调试 xff0c 使用了 ip 43 端口 的形式进行访问 xff0c 并且未配置账号密码鉴权机制 在日常测试一段时间后 xff0c 终于还是收到了来自腾讯云的监管通知 xff0c 说服务存在数
  • RoboMaster机器人运行教程(一)

    1 环境配置 系统 xff1a ubuntu16 04 xff0c 安装ROS 2 基础学习 需要C 43 43 和python基础 xff0c 和ROS的基础知识 xff0c 网上有很多教程 xff0c 推荐知乎大佬教程 xff1a 我的
  • slambook2+ch7+pose_estimation_2d2d+估计多张图像之间的位姿

    算法 计算第一张图和第二张图的关键点并匹配以第一张图的相机坐标为世界坐标 xff0c 计算第二张图相对第一张图的旋转矩阵 平移矩阵不断更新第一张图 xff0c 在进行第二次计算时 xff0c 以第二张图为第一张图 xff0c 以第二张图的相
  • 重做Unbuntu 18.0.43 LTS系统 并为SLAM配置环境

    目录 前言 一 安装列表 1 Ubuntu 18 0 43 LTS 1 0 A 搜狗输入法 1 0 B ibus输入法安装 1 1 更换软件源 1 2 安装vim curl等工具 1 3 安装浏览器Chrome git等 1 4 安装g 4
  • PostMan各个版本下载

    打开地址 xff1a https gitee com hlmd PostmanCn
  • 快速解决matlab出现错误使用mex,未找到支持的编译器或 SDK的提示

    matlab mex命令提示找不到编译器或SDK 参考博客 xff1a https blog csdn net cfqcfqcfqcfqcfq article details 63295746 utm source 61 blogxgwz1
  • linux 串口应用层API

    include lt termios h gt struct termios oldtio newtio fd 61 open dev tty0 O RDWR O NOCTTY tcgetattr fd amp oldtio 获取终端参数
  • 2022年中国研究生数学建模竞赛B题-方形件组批优化问题

    一 背景介绍 智能制造被 中国制造2025 列为主攻方向 而个性化定制 更短的产品及系统生命周期 互联互通的服务模式等成为目前企业在智能制造转型中的主要竞争点 以离散行业中的产品为例 xff0c 如电子器件 汽车 航空航天零部件等 xff0
  • 无线网络知识、WiFi原理

    无线网络 B站链接 一 电磁波的传输 电磁波传播方式 地波 xff08 低于2MHZ xff09 天波 2MHZ 30MHZ 直线波 30MHZ以上 电磁波的发射与接收装置 天线 作用 xff1a 将电磁波辐射到空间中或收集电磁波 辐射模式
  • yolov5输出检测到的目标坐标信息

    找到detect py文件 span class token keyword for span span class token operator span xyxy span class token punctuation span co
  • TCP之 select模型

    前记 xff1a select模型主要用于解决tcp通信中 xff0c 每次处理一个独立的客户都要单独的开线程 xff0c 这样会导致客户连接数很大时 xff0c 线程数也会很多 而使用select就会将线程缩减至2个 xff0c 一个主线
  • ROS入门:GPS坐标转换&Rviz显示轨迹

    GPS信息是无法直接绘制轨迹的 xff0c 因为其x xff0c y为经纬度 xff0c z为高度 xff0c 单位不一样 xff0c 本程序实现了以下功能 xff1a 1 将GPS轨迹 xff0c 从经纬度WGS 84坐标转换到真实世界x
  • ubuntu实用技巧

    ubuntu 截图 xff03 保存到图片文件夹 Print Screen 截取整个桌面 Alt 43 Print Screen 截取选中的窗口 Shift 43 Print Screen 自由选区 xff03 复制到剪贴板 Ctrl 43
  • 在ThinkPad X280加装M.2硬盘上安装 Ubuntu 18.04.3 填坑记录

    填坑背景 用了一段时间的X280后 xff0c 突然想在M 2接口上加装一个 NVMe 2242 的SSD xff0c 发现 Lenovo 的BIOS设置的非常奇特 能够检测到这个硬盘 xff0c 但是启动项里就是不能识别 xff01 或许
  • sip注册示例

    这里给出一个sip注册的示例 xff0c 其中平台注册的密码为12345678 xff0c 供相关开发参考 REGISTER sip 34020000002000000001 64 192 168 88 119 SIP 2 0 Via SI
  • spring security验证流程

    工作需要 xff0c 又弄起了权限的管理 虽然很早以前都了解过基于容器的权限实现方式 xff0c 但是一直都觉得那东西太简陋了 后来使用liferay时发现它的权限系统的确做得很优秀 xff0c 感觉这也可能是它做得最出色的地方吧 但是当时