`

我的SpringSecurity实践

阅读更多
我的SpringSecurity实践

(一) 数据库与实体类设计(mysql)

-- 权限
DROP TABLE IF EXISTS `me`.`tbl_permission` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_permission` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_name` VARCHAR(45) NOT NULL ,
  `_desc` VARCHAR(255) NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_name_UNIQUE` (`_name` ASC) )
ENGINE = InnoDB;

-- 角色
DROP TABLE IF EXISTS `me`.`tbl_role` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_role` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_name` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_name_UNIQUE` (`_name` ASC) )
ENGINE = InnoDB;

-- 角色权限关联表
DROP TABLE IF EXISTS `me`.`tbl_role_permission` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_role_permission` (
  `_id_permission` INT NOT NULL ,
  `_id_role` INT NOT NULL ,
  PRIMARY KEY (`_id_permission`, `_id_role`) ,
  INDEX `fk_permission_QENOQPN` (`_id_permission` ASC) ,
  INDEX `fk_role_PQEMAQWENGHJ` (`_id_role` ASC) ,
  CONSTRAINT `fk_permission_QENOQPN`
    FOREIGN KEY (`_id_permission` )
    REFERENCES `me`.`tbl_permission` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_role_PQEMAQWENGHJ`
    FOREIGN KEY (`_id_role` )
    REFERENCES `me`.`tbl_role` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- 用户表
DROP TABLE IF EXISTS `me`.`tbl_user` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_user` (
  `_id` INT NOT NULL AUTO_INCREMENT ,
  `_username` VARCHAR(45) NOT NULL ,
  `_password` CHAR(32) NULL ,
  `_disabled` CHAR(1) NULL ,
  PRIMARY KEY (`_id`) ,
  UNIQUE INDEX `_username_UNIQUE` (`_username` ASC) ,
  UNIQUE INDEX `_disabled_UNIQUE` (`_disabled` ASC) )
ENGINE = InnoDB;

-- 用户Email表 跟SpringSecurity没有关系
DROP TABLE IF EXISTS `me`.`tbl_email` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_email` (
  `_user_id` INT NOT NULL ,
  `_email` VARCHAR(70) NOT NULL ,
  `_order` INT NOT NULL ,
  PRIMARY KEY (`_user_id`, `_email`) ,
  UNIQUE INDEX `_email_UNIQUE` (`_email` ASC) ,
  INDEX `fk_user_id_QWZLAKUIG` (`_user_id` ASC) ,
  CONSTRAINT `fk_user_id_QWZLAKUIG`
    FOREIGN KEY (`_user_id` )
    REFERENCES `me`.`tbl_user` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- 用户角色关联表
DROP TABLE IF EXISTS `me`.`tbl_user_role` ;

CREATE  TABLE IF NOT EXISTS `me`.`tbl_user_role` (
  `_user_id` INT NOT NULL ,
  `_role_id` INT NOT NULL ,
  PRIMARY KEY (`_user_id`, `_role_id`) ,
  INDEX `fk_user_id_POIUYHJNB` (`_user_id` ASC) ,
  INDEX `fk_role_id_MNHTRFVD` (`_role_id` ASC) ,
  CONSTRAINT `fk_user_id_POIUYHJNB`
    FOREIGN KEY (`_user_id` )
    REFERENCES `me`.`tbl_user` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_role_id_MNHTRFVD`
    FOREIGN KEY (`_role_id` )
    REFERENCES `me`.`tbl_role` (`_id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

显然这种设计方式实际上比较常见的,用户与角色是多对多关系;角色与权限也是多对多关系。

下面是实体类(为了节约篇幅getter和setter以及ORM相关的元注释略去)
import java.io.Serializable;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;

/** 权限类实现 GrantedAuthority接口 */
public class Permission implements Serializable, GrantedAuthority {
	private Integer id;
	private String name;
	private String description;
	private Set<Role> roles;

	public String getAuthority() {
		return getName();
	}
}

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;

/** 角色类 */
public class Role implements Serializable {
	private Integer id;
	private String name;
	private Set<Permission> permissions = new HashSet<Permission>();
	private Set<User> users = new HashSet<User>();
	
	// 为了简便起见 ROLE 和 Permission都视为一种权限
	public GrantedAuthority generateGrantedAuthority() {
		return new GrantedAuthority() {
			public String getAuthority() {
				return getName();
			}
		};
	}
}

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/** 用户类实现UserDetails接口 */
public class User implements Serializable, UserDetails {
	private Integer id;
	private String username;
	private String password;
	private List<String> emails = new ArrayList<String>();	// 和SpringSecurity没有关系的业务字段
	private String disabled;
	private Set<Role> roles = new HashSet<Role>();

	public Collection<? extends GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> list = new ArrayList<GrantedAuthority>();
		
		for (Role role : roles) {
			list.add(role.generateGrantedAuthority());
			for (Permission permission : role.getPermissions()) {
				list.add(permission);
			}
		}
		// 排序其实没有必要
		// Collections.sort(list, GrantedAuthorityComparators.REVERSE);
		return list;
	}

	public String getPassword() {
		return password;
	}

	public String getUsername() {
		return username;
	}

	public boolean isAccountNonExpired() {
		return true;
	}

	public boolean isAccountNonLocked() {
		return true;
	}

	public boolean isCredentialsNonExpired() {
		return true;
	}

	public boolean isEnabled() {
		return disabled.equalsIgnoreCase("F");
	}
}

class GrantedAuthorityComparators implements Comparator<GrantedAuthority> {
	
	public static final Comparator<GrantedAuthority> DEFAULT = new GrantedAuthorityComparators();
	
	public static final Comparator<GrantedAuthority> REVERSE = new Comparator<GrantedAuthority>() {
		public int compare(GrantedAuthority o1, GrantedAuthority o2) {
			return - DEFAULT.compare(o1, o2);
		}
	};

	private GrantedAuthorityComparators() { super(); }

	public int compare(GrantedAuthority g1, GrantedAuthority g2) {
		return g1.getAuthority().compareTo(g2.getAuthority());
	}
}


(二) 编写重要的UserDetailsService实现类
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.yingzhuo.me.domain.User;

@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public interface UserDao extends UserDetailsService {
	public User findUserByUsernameAndPassword(String username, String password);
}

import org.hibernate.Query;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Repository;

@Repository("userDao")
public class UserDaoImpl extends BaseDao implements UserDao {

	public User findUserByUsernameAndPassword(String username, String password) {
		final String hql = "from User as u left join fetch u.roles where u.username = :username and u.password = :password";

		Query query = getSession().createQuery(hql)
				.setParameter("username", username)
				.setParameter("password", password);
		return (User) query.uniqueResult();
	}

	public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
		final String hql = "from User as u left join fetch u.roles where u.username = :username";

		Query query = getSession().createQuery(hql).setParameter("username", username);
		User user = (User) query.uniqueResult();

		if (user == null) {
			throw new UsernameNotFoundException("Username '" + username + "' not found");
		}

		// 取消延迟加载
		for (Role role : user.getRoles()) {
			for (Permission per : role.getPermissions()) {
				per.getName();
			}
		}
		return user;
	}
}

虽然SpringSecurity框架提供的接口很多,真正要亲自实现的不多三个而已
  • org.springframework.security.core.GrantedAuthority
  • org.springframework.security.core.userdetails.UserDetails
  • org.springframework.security.core.userdetails.UserDetailsService


(三) 编写Spring Security 配置文件
尽管SpringSecurity提供了<http>元素来简化配置,简化过后有相当多的细节被隐藏起来了。
有时候想改变一下框架的默认行为十分不便。我还是用传统的Bean方式。
这样虽然麻烦,但是掌握之后,一旦看哪个bean不顺眼就可以方便的取而代之。强大灵活!

3.0 xml的schema, 因为是用bean的方式配置嘛,默认命名空间当然是用beans方便。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:c="http://www.springframework.org/schema/c"
	xmlns:util="http://www.springframework.org/schema/util"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:security="http://www.springframework.org/schema/security"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
		http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.1.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
</beans>


3.1 web项目部署描述
<!-- Spring Security 核心拦截器组 -->
<filter>
	<filter-name>springSecurityFilterChain</filter-name>
	<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
	<filter-name>springSecurityFilterChain</filter-name>
	<url-pattern>/*</url-pattern>
</filter-mapping>

<!-- Web Application Context -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>
		classpath:spring-bean.xml
		classpath:spring-orm.xml
		classpath:spring-security.xml
	</param-value>
</context-param>

<!-- 支持SpringSecurity Session并发控制 -->
<listener>
	<listener-class>org.springframework.security.web.session.HttpSessionEventPublisher</listener-class>
</listener>

提醒一下,如果SpringSecurity和Struts2共同使用的话 org.springframework.web.filter.DelegatingFilterProxy 一定要配置在
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter前面。要不然你的SpringSecurity根本不会起任何作用的。
当然如果使用的是 SpringMVC框架的话那根本无所谓配置顺序,因为SpringMVC的核心转发器是一个Servlet的实现。


3.2 配置SpringSecurity过滤器组 (我一般就用这九个) 顺序很重要
<bean id="springSecurityFilterChain" class="org.springframework.security.web.FilterChainProxy">
	<security:filter-chain-map request-matcher="ant" >
	<security:filter-chain pattern="/**" filters="
	   channelProcessingFilter,
           concurrencyFilter,
           securityContextPersistenceFilter,
           logoutFilter,
           usernamePasswordProcessingFilter,
           rememberMeProcessingFilter,
           anonymousProcessingFilter,
           exceptionTranslationFilter,
           filterSecurityInterceptor" />
	</security:filter-chain-map>
</bean>


3.3 channelProcessingFilter: 常用来将某些HTTP协议的URL重定向到HTTPS协议
<bean id="channelProcessingFilter" class="org.springframework.security.web.access.channel.ChannelProcessingFilter">
	<property name="channelDecisionManager" ref="channelDecisionManager" />
	<property name="securityMetadataSource">
		<security:filter-security-metadata-source request-matcher="ant">
			<!--
			<security:intercept-url pattern="/just/test" access="REQUIRES_SECURE_CHANNEL" />
			-->
			<security:intercept-url pattern="/**" access="ANY_CHANNEL" />
		</security:filter-security-metadata-source>
	</property>
</bean>

<bean id="channelDecisionManager"
	class="org.springframework.security.web.access.channel.ChannelDecisionManagerImpl">
	<property name="channelProcessors">
		<list>
			<ref local="secureChannelProcessor" />
			<ref local="insecureChannelProcessor" />
		</list>
	</property>
</bean>

<bean id="secureChannelProcessor" class="org.springframework.security.web.access.channel.SecureChannelProcessor" />
<bean id="insecureChannelProcessor" class="org.springframework.security.web.access.channel.InsecureChannelProcessor" />


3.4 concurrencyFilter:HttpSession并发过滤器
<bean id="concurrencyFilter" class="org.springframework.security.web.session.ConcurrentSessionFilter">
	<property name="sessionRegistry" ref="sessionRegistry" />
	<property name="expiredUrl" value="/common/session-expired" />	<!-- 配置Session过期后重定向的地址 -->
</bean>

<bean id="sessionRegistry" class="org.springframework.security.core.session.SessionRegistryImpl" />

<bean id="sas" class="org.springframework.security.web.authentication.session.ConcurrentSessionControlStrategy">
	<constructor-arg index="0" ref="sessionRegistry" />
	<property name="maximumSessions" value="1" />
</bean>


3.5 securityContextPersistenceFilter:获取或存储一个SecurityContext
<bean id="securityContextPersistenceFilter" class="org.springframework.security.web.context.SecurityContextPersistenceFilter" />


3.6 logoutFilter:监控一个实现退出功能的URL
<bean id="logoutFilter" class="org.springframework.security.web.authentication.logout.LogoutFilter">
	<constructor-arg index="0" value="/common/login" />	<!-- 退出后重定向 -->
	<constructor-arg index="1">
		<array>
			<ref local="logoutHandler" />
			<ref local="rememberMeServices" />
		</array>
	</constructor-arg>
	<property name="filterProcessesUrl" value="/common/logout"/> <!-- 监控的URL -->
</bean>

<!-- 这个Bean注入到logoutFilter中去,它实际负责最后的扫尾工作,如把HttpSession实例删除 -->
<bean id="logoutHandler" class="org.springframework.security.web.authentication.logout.SecurityContextLogoutHandler">
	<property name="invalidateHttpSession" value="true" />
</bean>

这个bean没有没有默认构造方法。

3.7 usernamePasswordProcessingFilter:处理用户登录请求
<bean id="usernamePasswordProcessingFilter" class="org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter">
	<property name="filterProcessesUrl" value="/common/login-process"/>
	<property name="usernameParameter" value="username"/>
	<property name="passwordParameter" value="password"/>
	<property name="authenticationManager" ref="customAuthenticationManager"/>
	<property name="rememberMeServices" ref="rememberMeServices"/>
	<property name="authenticationFailureHandler" ref="authenticationFailureHandler"/>
	<property name="sessionAuthenticationStrategy" ref="sas" />
</bean>

<!--
	这个Bean注入到usernamePasswordProcessingFilter中去,他决定用户名和密码验证失败之后的动作
	注意: 应设置行为为转发方式,否则保存在HttpServletRequest实例中的错误信息会因为重定向而丢失。
 -->
<bean id="authenticationFailureHandler" class="org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler">
	<property name="defaultFailureUrl" value="/common/login"/>
	<property name="useForward" value="true" />
</bean>


3.8 rememberMeProcessingFilter: 实现"记住我"功能
<bean id="rememberMeProcessingFilter" class="org.springframework.security.web.authentication.rememberme.RememberMeAuthenticationFilter">
	<property name="rememberMeServices" ref="rememberMeServices"/>
	<property name="authenticationManager" ref="customAuthenticationManager" />
</bean>

<bean id="rememberMeServices" class="org.springframework.security.web.authentication.rememberme.PersistentTokenBasedRememberMeServices">
	<property name="key" value="#{securityKeys['remember-me']}" />	<!-- KEY 用于加密 两个一定要相同 -->
	<property name="parameter" value="_remember_me" />
	<property name="tokenValiditySeconds" value="7200" />
	<property name="tokenRepository" ref="inMemoryTokenRepository" />
	<!-- 下面就是我自己实现的UserDetailsService 我给了alias "hibernateUserDetailsService" -->
	<property name="userDetailsService" ref="hibernateUserDetailsService" />
</bean>

<bean id="rememberMeAuthenticationProvider"
	class="org.springframework.security.authentication.RememberMeAuthenticationProvider">
	<property name="key" value="#{securityKeys['remember-me']}" /> <!-- KEY 用于加密 两个一定要相同 -->
</bean>

<bean id="inMemoryTokenRepository" class="org.springframework.security.web.authentication.rememberme.InMemoryTokenRepositoryImpl" />

<!--  
<bean id="jdbcRememberMeTokenRepository"
	class="org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl">
	<property name="dataSource" ref="dataSource" />
</bean>
-->

被我注释掉的jdbcRememberMeTokenRepository需要一个数据库表
这个表用来持久化RememberMeToken,生产环境一般还是要这样做的。
create table persistent_logins (
username varchar(64) not null,
series varchar(64) primary key,
token varchar(64) not null,
last_used timestamp not null
);


3.9 anonymousProcessingFilter:如果用户不能通过验证则给添加一个匿名用户的角色
<bean id="anonymousProcessingFilter" class="org.springframework.security.web.authentication.AnonymousAuthenticationFilter">
	<property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS"/>
	<property name="key" value="#{securityKeys['anonymous']}"/>
</bean>


3.10 exceptionTranslationFilter: 验证通不过?没有访问权限?这个Filter决定如果出现异常了到底应该这么办。
<bean id="exceptionTranslationFilter" class="org.springframework.security.web.access.ExceptionTranslationFilter">
	<property name="authenticationEntryPoint" ref="authenticationEntryPoint" />
	<property name="accessDeniedHandler" ref="accessDeniedHandler" />
</bean>

<bean id="authenticationEntryPoint" class="org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint">
	<property name="useForward" value="false" />
	<property name="loginFormUrl" value="/common/login" />
</bean>

<bean id="accessDeniedHandler" class="org.springframework.security.web.access.AccessDeniedHandlerImpl">
	<property name="errorPage" value="/common/error"/>
</bean>


3.11 filterSecurityInterceptor:核心过滤器的最后一个。它完成最终的授权判断
下面的配置有点多,那是因为filterSecurityInterceptor是个懒家伙。
它把工作委托AuthenticationManager接口,AuthenticationManager接口也不真的干活,
它委托多个AuthenticationProvider接口,当然其中一个AuthenticationProvider还是要
把工作委托给我们的UserDetailsService实现的。最后投票决定到最终结果。
<bean id="filterSecurityInterceptor" class="org.springframework.security.web.access.intercept.FilterSecurityInterceptor">
	<property name="authenticationManager" ref="customAuthenticationManager" />
	<property name="accessDecisionManager" ref="affirmativeBased" />
	<property name="securityMetadataSource">
		<security:filter-security-metadata-source use-expressions="true">
			<security:intercept-url pattern="/common/login" access="permitAll" />
			<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />
		</security:filter-security-metadata-source>
	</property>
</bean>

<bean id="customAuthenticationManager" class="org.springframework.security.authentication.ProviderManager">
	<property name="authenticationEventPublisher" ref="defaultAuthEventPublisher"/>
	<property name="providers">
		<list>
			<ref local="daoAuthenticationProvider"/>
			<ref local="anonymousAuthenticationProvider"/>
			<ref local="rememberMeAuthenticationProvider"/>
		</list>
	</property>
</bean>

<!--
	这个Bean决定了投票策略,decisionVoters只要有任意一个决定通过,那么结果就是通过。
-->
<bean class="org.springframework.security.access.vote.AffirmativeBased" id="affirmativeBased">
	<property name="decisionVoters">
		<list>
			<ref bean="roleVoter"/>
			<ref bean="expressionVoter"/>
			<ref bean="authenticatedVoter"/>
		</list>
	</property>
</bean>

<bean class="org.springframework.security.access.vote.RoleVoter" id="roleVoter" />
<bean class="org.springframework.security.access.vote.AuthenticatedVoter" id="authenticatedVoter" />

<bean id="defaultAuthEventPublisher" class="org.springframework.security.authentication.DefaultAuthenticationEventPublisher"/>

<bean id="daoAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
	<property name="passwordEncoder" ref="md5PasswordEncoder"/>
	<property name="userDetailsService" ref="hibernateUserDetailsService" />
	<!-- 
	<property name="saltSource" ref="saltSource"/>
	-->
</bean>

<bean id="anonymousAuthenticationProvider" class="org.springframework.security.authentication.AnonymousAuthenticationProvider">
	<property name="key" value="#{securityKeys['anonymous']}" />
</bean>

<bean class="org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler" id="expressionHandler"/>

<bean class="org.springframework.security.web.access.expression.WebExpressionVoter" id="expressionVoter">
	<property name="expressionHandler" ref="expressionHandler"/>
</bean>


3.12 其他的工具bean
<util:properties id="securityKeys">
	<prop key="remember-me">182301IEKO1L73C181891TLTKABCNKA1956A7G9UPQXN</prop>
	<prop key="anonymous">BF93JFJ091N00Q7HF</prop>
</util:properties>

<alias name="userDao" alias="hibernateUserDetailsService"/>

<bean class="org.springframework.security.authentication.encoding.Md5PasswordEncoder" id="md5PasswordEncoder" />

<!-- 如果使用 Sha加密的话,可以用一用 -->
<!--
<bean class="org.springframework.security.authentication.dao.ReflectionSaltSource" id="saltSource">
	<property name="userPropertyToUse" value="id"/>
</bean>
-->


(四) How to
4.1 在MVC框架里,我该如何得到现在已经登录的用户?
User currentUser = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();


4.2 登录页面我要做后端验证,我该怎么办?
usernamePasswordProcessingFilter实际完成这个功能,自己写这个类。
public class ValidatedUsernamePasswordAuthenticationFilter extends UsernamePasswordAuthenticationFilter {

	private Logger logger = LoggerFactory.getLogger(getClass());
	
	@Override
	public Authentication attemptAuthentication(HttpServletRequest request,
			HttpServletResponse response) throws AuthenticationException {

		Authentication result = null;
		Locale locale = request.getLocale();

		try {
			result = super.attemptAuthentication(request, response);
			logger.debug("登录成功");
		} catch (AuthenticationException failed) {
			logger.debug("登录失败");
			String msg = super.messages.getMessage("validator.login.fail", locale);
			request.setAttribute("fail", msg);
			throw failed;
		}
		return result;
	}
}
// 注意这个类是实现了MessageSourceAware接口的。你加载了国际化文件的MessageSource用就好了。但是一定要配置在 WebApplicationContext里,不要配置在springMVC特有的配置文件里。
分享到:
评论
10 楼 wellbeing_wang 2013-12-22  
你好:
   这个相关源码能否提供一份?谢谢!wangwenbincn@163.com
9 楼 guang.027 2013-12-11  
能否给相关源,谢谢,flash8627@hotmail.com
8 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
我就是用的这个呀。

<sec:authorize access="hasRole('ROLE_ADMIN')">
这段文本只有管理员可见
</sec:authorize>

如果登录的用户有管理员的角色的话,中间的文本他就可以看见了。


那您就把ROLE_ADMIN角色写死在jsp中
ROLE_ADMIN角色删除不了
如果其他角色想访问该资源,还得改源码
小弟糊涂,还望大·侠指点!
方便的话加我qq459109544
谢谢
7 楼 yingzhor 2012-05-10  
我就是用的这个呀。

<sec:authorize access="hasRole('ROLE_ADMIN')">
这段文本只有管理员可见
</sec:authorize>

如果登录的用户有管理员的角色的话,中间的文本他就可以看见了。
6 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
leon.s.kennedy 写道
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!


你好。
"PERMISSION_CAN_DO_SOMETHING"就是"GrantedAuthority"接口的"generateGrantedAuthority()"方法的返回值,我是用Hibernate实现的UserDetailsService从数据库里把这个字符串取出来。

这个字符串就是一个用户有没有某种权限判断的依据。

<sec:authorize url="/add*">我也没有用过。


我现在是把资源url写在DB中,配置文件中不需要
也是自定义的filter
使用ss3提供的JSP标签,<sec:authorize 来控制页面上显示的内容,请问您是怎么做到的?
5 楼 yingzhor 2012-05-10  
leon.s.kennedy 写道
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!


你好。
"PERMISSION_CAN_DO_SOMETHING"就是"GrantedAuthority"接口的"generateGrantedAuthority()"方法的返回值,我是用Hibernate实现的UserDetailsService从数据库里把这个字符串取出来。

这个字符串就是一个用户有没有某种权限判断的依据。

<sec:authorize url="/add*">我也没有用过。
4 楼 leon.s.kennedy 2012-05-10  
yingzhor 写道
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。


请问<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />
PERMISSION_CAN_DO_SOMETHING是表达什么意思?写死在这里吗?

页面标签<sec:authorize url="/add*">不好使,看官方文档说在应用程序上下文必须有webapplicationContext实例,才能用 google上查不到 请帮忙解答,非常感谢!
3 楼 yingzhor 2012-05-03  
引用

角色写死在配置文件里,如果今后角色是可以维护的怎办


<security:intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')" />


以上的配置只是例子而已。

你可以这样的
<security:intercept-url pattern="/**" access="hasRole('PERMISSION_CAN_DO_SOMETHING')" />


hasRole是一个SpEL的函数名,个人也感觉它起名字有问题,这个函数实际上是根据
GrantedAuthority接口来判断。而跟我们定义的Role或者Permission没有直接关系。
2 楼 yingzhor 2012-05-03  
我不太了解你所指的“把角色写死在配置文件里”是什么意思。

我并没有这样做,GrantedAuthority的实现类是Permission 而不是 Role。
而Permission也是数据库相关的实体类。也就是说,实际上权限的信息是存放在数据库里的。

因为我的文章只是做个简单的Demo, 我其实偷换了一下概念,让Role和Permission都具有了
GrantedAuthority定义的语义。在生产上我们不会这么做。

引用

前台页面标签中,如何控制显示连接?也把角色写死吗?

SpringSecurity提供了JSP标签,用来决定显示/隐藏页面的内容。

<security:authorize /> 具体使用方法请查看官方文档。

引用

还用种匹配url的标签,您不妨试试看

谢谢,我知道Spring Security还特别提供了一个security的xml schema,
但是个人觉得使用那样的配置方式虽然简单,但是隐藏的细节太多。

有时候想改变一下spring security框架的默认行为就不是那么方便了。
1 楼 leon.s.kennedy 2012-05-03  
博主您好
您如此配置ss3文件,把角色写死在配置文件里,如果今后角色是可以维护的怎办?
前台页面标签中,如何控制显示连接?也把角色写死吗?
还用种匹配url的标签,您不妨试试看

相关推荐

    Spring Security应用实践,整合redis缓存

    小项目有Shiro的比较多,因为相比与SpringSecurity,Shiro的上手更加的简单。 一般Web应用的需要进行认证和授权。 ​认证:验证当前访问系统的是不是本系统的用户,并且要确认具体是哪个用户 ​授权:经过认证后...

    Spring Security3.1实践

    NULL 博文链接:https://aokunsang.iteye.com/blog/1638558

    全套Spring Security入门到项目实战课程

    Spring Security框架介绍 Spring Security认证与授权机制 Spring Security安全性解决方案 Spring Security权限控制实现 Spring Security与Web应用安全 ...Spring Security最佳实践 Spring Security安全漏洞与防范

    详细使用SpringSecurity

    第 I 部分 “基础篇”。环境搭建,进行最简单的配置。 第 II 部分 “保护web篇”。谈谈对url的权限控制。 第 III 部分 “内部机制篇”。对方法调用进行权限控制。...包含最佳实践,可以当做是OA里权限模块的总结。

    Spring Security.pdf

    SpringSecuriye全方位分析-入门和实践

    SpringSecurity的Web应用和指纹登录实践

    Java开发人员在解决Web应用安全相关的问题时,通常会采用两个非常流行的安全框架,Shiro和SpringSecurity。Shiro配置简单,上手快,满足一般应用的安全需求,但是功能相对单一。SpringSecurity安全粒度细,与Spring...

    springsecurity:Spring安全学习总结

    在实践中摸索出了一套结合JSON +智威汤逊(JSON网络令牌)+Spring引导+ Spring Security的技术的权限方案趁着国庆假期记录一下。 内容 生成jwt 解析jwt 根据1,2实现对访问路径的权限拦截 使用 spring security 学习...

    Spring Security3实践总结

    NULL 博文链接:https://kingxss.iteye.com/blog/1908011

    calm-cloud:实践搭建:springcloud + nacos +网关+ springsecurity

    实践建造springcloud的maven工程 项目中的技术选型是: 服务注册中心,服务配置: 纳科斯 服务之间的调用: 开放式 服务网关: 网关 用户登录: Spring安全 持久层框架: mybatis-plus 数据库驱动: mybatis-...

    spring_security_management.zip

    认证用户的过程:进入认证页面--&gt;输入用户名和密码--&gt;CSRF--&gt;查询存储的用户数据(用户名、密码以及角色信息)--&gt;认证完成 进行实践的项目源代码。

    SpringSecurity深度解析与实践(3)

    项目资源

    (SSM、Spring Boot、Spring Security、MinIO、Vue). 以Java项目实践

    在这个平台上,我们为大家带来了一系列的 JavaSSM(Spring + SpringMVC + MyBatis)项目。这些项目旨在展示SSM框架在实际应用中的魅力,同时也为开发者提供了一个快速学习和实践的机会。通过下载和使用这些项目,您...

    (SSM、Spring Boot、Spring Security、MinIO、Vue)各种项目实践,并附上高质量文章讲解

    SSM(Spring + Spring MVC + MyBatis)框架作为Java开发中的黄金组合,为开发者提供了强大的技术支持和丰富的功能。本系列资料将带您从零基础开始,逐步掌握SSM的核心技术和最佳实践,助您在Java Web开发领域更上一...

    SpringMVC-SpringSecurity-MySql-Hibernate

    配置Spring Security并创建经过身份验证的页面,例如“登录”页面和“管理页面” 。 用于在具有授权角色的数据库上创建安全用户的安全上下文集成。 (本教程使用的是JNDI数据源; &lt;jee:jndi-lookup id=...

    JavaPracticeProjects(SSM、SpringBoot、SpringSecurity、MinIO、Vue)以Ja

    JavaPracticeProjects(SSM、SpringBoot、SpringSecurity、MinIO、Vue).以Java语言为主的各种项目实践,涵盖各个业务、各个功能,让你再也不用寻找各种框架demo、项.zip

    ssm框架整合Spring Security项目.zip

    SSM(Spring + Spring MVC + MyBatis)框架作为Java开发中的黄金组合,为开发者提供了强大的技术支持和丰富的功能。本系列资料将带您从零基础开始,逐步掌握SSM的核心技术和最佳实践,助您在Java Web开发领域更上一...

    [后端开发] Spring boot Security开发安全的REST服务 视频教程 完整压缩包

    [后端开发] Spring boot Security开发安全的REST服务 视频教程 系统学习完该视频教程后,你将成为J2EE 微服务框架Springboot的最佳实践者 我的CSDN博客地址: ...

Global site tag (gtag.js) - Google Analytics