Spring Security简单Demo

 

Spring Security简单Demo

1.创建项目且引入相关的.jar文件

spring-security-acl-2.0.5.jar

spring-security-core-2.0.5.jar

spring-security-taglibs-2.0.5.jar

2.创建相应的实体类:用户、角色、资源

2.1用户类:User.java需要实现UserDetails接口,具体内容如下:

package com.zsw.entity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Proxy;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.userdetails.UserDetails;
/**
 * 用户类
 * @author Administrator
 */

@Entity
@Proxy(lazy = false)
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public  class User  implements UserDetails {
 
  private  static  final  long serialVersionUID = 8026813053768023527L;
  /**
  * 编号ID
  */

    @Id
 @GeneratedValue
  private Integer id;
  private  String name;  //用户名
 
  private  String password;  //密码
 
  private  boolean disabled;  //是否关闭
 
  /**
  * 用户所有的角色
  */

 @ManyToMany(targetEntity = Role. class, fetch = FetchType.EAGER)
    @JoinTable(name =  "user_role", joinColumns = @JoinColumn(name =  "user_id"), inverseJoinColumns = @JoinColumn(name =  "role_id"))
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  private Set<Role> roles;
 
 @Transient
  private Map< String, List<Resource>> roleResources;
 
  /**
  * The default constructor
  */

  public User() {
 }
  /**
  * 根据User返回这个User所拥有的权限列表
  */

  public GrantedAuthority[] getAuthorities() {
  List<GrantedAuthority> grantedAuthorities =  new ArrayList<GrantedAuthority>(roles.size());
      for(Role role : roles) {
      grantedAuthorities.add( new GrantedAuthorityImpl(role.getName()));
     }
         return grantedAuthorities.toArray( new GrantedAuthority[roles.size()]);
 }
 
  /**
  * Returns the authorites string
  * 
  * eg. 
  *    downpour --- ROLE_ADMIN,ROLE_USER
  *    robbin --- ROLE_ADMIN
  * 
  * @return
  */

  public  String getAuthoritiesString() {
     List< String> authorities =  new ArrayList< String>();
      for(GrantedAuthority authority :  this.getAuthorities()) {
         authorities.add(authority.getAuthority());
     }
      return StringUtils.join(authorities,  ",");
 }
  public  String getPassword() {
   return password;
 }
  public  String getUsername() {
   return name;
 }
  public  boolean isAccountNonExpired() {
   return true;
 }
  public  boolean isAccountNonLocked() {
   return true;
 }
  public  boolean isCredentialsNonExpired() {
   return true;
 }
  public  boolean isEnabled() {
   return !disabled;
 }
  public Integer getId() {
   return id;
 }
  public  String getName() {
   return name;
 }
  public  boolean isDisabled() {
   return disabled;
 }
  public Set<Role> getRoles() {
   return roles;
 }
 
  /**
  * 在User对象中设置一个缓存机制,在第一次取的时候,
  * 通过遍历User所有的Role,获取相应的Resource信息。
  * @return
  */

  public Map< String, List<Resource>> getRoleResources() {
   // init roleResources for the first time
   if( this.roleResources == null) {
   
    this.roleResources =  new HashMap< String, List<Resource>>();
   
    for(Role role :  this.roles) {
     String roleName = role.getName();
    Set<Resource> resources = role.getResources();
     for(Resource resource : resources) {
      String key = roleName +  "_" + resource.getType();
      if(! this.roleResources.containsKey(key)) {
       this.roleResources.put(key,  new ArrayList<Resource>());
     }
      this.roleResources.get(key).add(resource);     
    }
   }
   
  }
   return  this.roleResources;
 }
  public  void setId(Integer id) {
   this.id = id;
 }
  public  void setName( String name) {
   this.name = name;
 }
  public  void setPassword( String password) {
   this.password = password;
 }
  public  void setDisabled( boolean disabled) {
   this.disabled = disabled;
 }
  public  void setRoles(Set<Role> roles) {
   this.roles = roles;
 }
 
}

其中,接口UserDetails定义的方法如下:

public  interface UserDetails  extends Serializable {
    
    GrantedAuthority[] getAuthorities();
     String getPassword();
     String getUsername();
     boolean isAccountNonExpired();
     boolean isAccountNonLocked();
     boolean isCredentialsNonExpired();
     boolean isEnabled();
}

2.2角色:Role.java具体的内容如下:

package com.zsw.entity;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
 * 角色
 * @author Administrator
 */

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public  class Role {
 
 @Id
 @GeneratedValue
  private Integer id; 
  private  String name; 
  private  String description; 
 @ManyToMany(targetEntity = Resource. class, fetch = FetchType.EAGER)
    @JoinTable(name =  "role_resource", joinColumns = @JoinColumn(name =  "role_id"), inverseJoinColumns = @JoinColumn(name =  "resource_id"))
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  private Set<Resource> resources;
  public Role() {  
 }
  public Integer getId() {
   return id;
 }
  public  String getName() {
   return name;
 }
  public  String getDescription() {
   return description;
 }
  public Set<Resource> getResources() {
   return resources;
 }
  public  void setId(Integer id) {
   this.id = id;
 }
  public  void setName( String name) {
   this.name = name;
 }
  public  void setDescription( String description) {
   this.description = description;
 }
  public  void setResources(Set<Resource> resources) {
   this.resources = resources;
 }
}

2.3资源Resource.java具体的内容如下:

package com.zsw.entity;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.Transient;
import org.apache.commons.lang.StringUtils;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
/**
 * 资源
 * Resource可能分成多种类型,比如MENU,URL,METHOD等等
 */

@Entity
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public  class Resource {
 @Id
    @GeneratedValue
  private Integer id; 
  private  String type; 
  private  String value;
 
 @ManyToMany(mappedBy =  "resources", targetEntity = Role. class, fetch = FetchType.EAGER)
    @Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
  private Set<Role> roles;
 
  public Resource() {
 }
 
  /**
  * 获取资源中所有角色名称
  * @return
  */

    @Transient
  public  String getRoleAuthorities() {
     List< String> roleAuthorities =  new ArrayList< String>();
      for(Role role : roles) {
      roleAuthorities.add(role.getName());
     }
         return StringUtils.join(roleAuthorities,  ",");
    }
  public Integer getId() {
   return id;
 }
  public  String getType() {
   return type;
 }
  public  String getValue() {
   return value;
 }
  public Set<Role> getRoles() {
   return roles;
 }
  public  void setId(Integer id) {
   this.id = id;
 }
  public  void setType( String type) {
   this.type = type;
 }
  public  void setValue( String value) {
   this.value = value;
 }
  public  void setRoles(Set<Role> roles) {
   this.roles = roles;
 }
}

3.定义一个接口SecurityManager.java用于获取所有的资源信息

package com.zsw.security;
import java.util.Map;
/**
 * 安全管理
 * @author Administrator
 */

public  interface SecurityManager {     
  public Map< StringString> loadUrlAuthorities();        
}

其具体的实现类如下:

package com.zsw.security.support;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import com.zsw.entity.Resource;
import com.zsw.entity.User;
import com.zsw.security.SecurityManager;
@Service( "securityManager")
public  class SecurityManagerSupport  extends HibernateDaoSupport  implements UserDetailsService, SecurityManager {
    @Autowired
     public  void init(SessionFactory sessionFactory) {
         super.setSessionFactory(sessionFactory);
    }
    
     /**
     * 根据用户名获取用户对象
     * 实现了UserDetailsService接口中的loadUserByUsername方法
     */

     public UserDetails loadUserByUsername( String userName)  throws UsernameNotFoundException, DataAccessException {
        List<User> users = getHibernateTemplate().find( "FROM User user WHERE user.name = ? AND user.disabled = false", userName);
         if(users.isEmpty()) {
             throw  new UsernameNotFoundException( "User " + userName +  " has no GrantedAuthority");
        }
         return users.get(0);
    }
    
     /**
     * 获取所有资源,对应所有的角色
     */

     public Map< StringString> loadUrlAuthorities() {
        Map< StringString> urlAuthorities =  new HashMap< StringString>();
        List<Resource> urlResources = getHibernateTemplate().find( "FROM Resource resource WHERE resource.type = ?""URL");
         for(Resource resource : urlResources) {
            urlAuthorities.put(resource.getValue(), resource.getRoleAuthorities());
        }
         return urlAuthorities;
    }
}

4.创建一个监听器,在系统启动的时候,把所有的资源load到内存作为缓存

package com.zsw.web.loader;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.zsw.security.SecurityManager;
/**
 * ------------------------------------------------------------
 * 这是一个监听器
 * 主要有2个方法,一个是应用启动时执行,另一个是应用关闭时执行
 * 在系统启动的时候,把所有的资源load到内存作为缓存 
 * ------------------------------------------------------------
 * 监听器的使用实际上适用于取代那些响应用户请求的Servelt,
 * 所以Listener类中无须提供用户请求的方法,
 * Listener的作用是为整个WEB应用提供后台服务。
 * ------------------------------------------------------------
 * @author 周尚武
 */

public  class ServletContextLoaderListener  implements ServletContextListener {
  /**
  * 应用启动时,该方法调用
  * 将资源的存放在servletContext中
  */

  public  void contextInitialized(ServletContextEvent servletContextEvent) {
  ServletContext servletContext = servletContextEvent.getServletContext();
  SecurityManager securityManager =  this.getSecurityManager(servletContext);
  Map< StringString> urlAuthorities = securityManager.loadUrlAuthorities();
  servletContext.setAttribute( "urlAuthorities", urlAuthorities);
 }
  /**
     * 应用关闭时该方法调用
     */

  public  void contextDestroyed(ServletContextEvent servletContextEvent) {
  servletContextEvent.getServletContext().removeAttribute( "urlAuthorities");
 }
  protected SecurityManager getSecurityManager(ServletContext servletContext) {
   return (SecurityManager) WebApplicationContextUtils.getWebApplicationContext(servletContext).getBean( "securityManager");
 }
}

5.获取当前用户

package com.zsw.security.support;
import org.springframework.security.context.SecurityContextHolder;
import com.zsw.entity.User;
/**
 * Spring Security提供了一个线程安全的对象:SecurityContextHolder,
 * 通过这个对象,我们可以访问当前的登录用户
 * @author Administrator
 */

public  class SecurityUserHolder {
 
  /**
  * 获取当前用户
  * @return
  */

  public  static User getCurrentUser() {
   return (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
 }
}

6.对资源进行认证

package com.zsw.security.interceptor;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.ServletContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.security.ConfigAttributeDefinition;
import org.springframework.security.ConfigAttributeEditor;
import org.springframework.security.intercept.web.FilterInvocation;
import org.springframework.security.intercept.web.FilterInvocationDefinitionSource;
import org.springframework.security.util.AntUrlPathMatcher;
import org.springframework.security.util.RegexUrlPathMatcher;
import org.springframework.security.util.UrlMatcher;
/**
 * 资源拦截器
 * 编写自己的FilterInvocationDefinitionSource实现类,对资源进行认证 
 */

public  class SecureResourceFilterInvocationDefinitionSource  implements FilterInvocationDefinitionSource, InitializingBean {
    
     private UrlMatcher urlMatcher;
     private  boolean useAntPath = true;    
     private  boolean lowercaseComparisons = true;
    
     public  void setUseAntPath( boolean useAntPath) {
         this.useAntPath = useAntPath;
    }
     public  void setLowercaseComparisons( boolean lowercaseComparisons) {
         this.lowercaseComparisons = lowercaseComparisons;
    }
     public  void afterPropertiesSet()  throws  Exception {
     
         this.urlMatcher =  new RegexUrlPathMatcher();        
         if (useAntPath) {
             this.urlMatcher =  new AntUrlPathMatcher();
        }
        
         if ( "true".equals(lowercaseComparisons)) {
             if (! this.useAntPath) {
                ((RegexUrlPathMatcher)  this.urlMatcher).setRequiresLowerCaseUrl(true);
            }
        }  else  if ( "false".equals(lowercaseComparisons)) {
             if ( this.useAntPath) {
                ((AntUrlPathMatcher)  this.urlMatcher).setRequiresLowerCaseUrl(false);
            }
        }        
    }
     public ConfigAttributeDefinition getAttributes(Object filter)  throws IllegalArgumentException {
        
        FilterInvocation filterInvocation = (FilterInvocation) filter;
         String requestURI = filterInvocation.getRequestUrl();
        Map< StringString> urlAuthorities =  this.getUrlAuthorities(filterInvocation);        
         String grantedAuthorities = null;
         for(Iterator<Map.Entry< StringString>> iter = urlAuthorities.entrySet().iterator(); iter.hasNext();) {
            Map.Entry< StringString> entry = iter.next();
             String url = entry.getKey();
            
             if(urlMatcher.pathMatchesUrl(url, requestURI)) {
                grantedAuthorities = entry.getValue();
                 break;
            }            
        }
        
         if(grantedAuthorities != null) {
            ConfigAttributeEditor configAttrEditor =  new ConfigAttributeEditor();
            configAttrEditor.setAsText(grantedAuthorities);
             return (ConfigAttributeDefinition) configAttrEditor.getValue();
        }        
         return null;
    }
    @SuppressWarnings( "unchecked")
  public Collection getConfigAttributeDefinitions() {
         return null;
    }
    @SuppressWarnings( "unchecked")
  public  boolean supports(Class clazz) {
         return true;
    }
    @SuppressWarnings( "unchecked")
  private Map< StringString> getUrlAuthorities(FilterInvocation filterInvocation) {
        ServletContext servletContext = filterInvocation.getHttpRequest().getSession().getServletContext();
         return (Map< StringString>)servletContext.getAttribute( "urlAuthorities");
    }
}

7.创建登陆login页面及登陆成功后的index页面:

login.jsp

< %@ page language= "java"  contentType= "text/html; charset=UTF-8"  pageEncoding= "UTF-8" %>
< !DOCTYPE html PUBLIC  "-//W3C//DTD XHTML 1.0 Transitional//EN"   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd" >
< html xmlns= "http://www.w3.org/1999/xhtml"  lang= "zh-CN" >
< head>
 < title>Spring Security< /title>
< /head>
< body>
    < form method= "post"  id= "loginForm"  action= "${pageContext.request.contextPath}/j_spring_security_check" >
        < p>User Name:< /p>
        < p>< input type= "text"  name= "j_username"  id= "j_username"  />< /p>
        < p>Password:< /p>     
        < p>< input type= "password"  name= "j_password"  id= "j_password"  />< /p>
        < p>< input type= "submit"  value= "submit"  />< /p>
    < /form>      
< /body>
< /html

index.jsp

< p>
 < a href= "${pageContext.request.contextPath}/j_spring_security_logout" >logout< /a>
< /p>
< p>Hello ${currentUser.name}, your role is: ${currentUser.authoritiesString}< /p>

8.相关的配置文件:

8.1web.xml

< ?xml version= "1.0"  encoding= "UTF-8" ?>
< web-app id= "Yoda"  version= "2.4"  xmlns= "http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
"http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" >
 < display-name>Yoda< /display-name>
     <!--- Spring ApplicationContext Definition -->
    < context-param>
        < param-name>contextConfigLocation< /param-name>
        < param-value>/WEB-INF/classes/applicationContext-*.xml< /param-value>
    < /context-param>
    
     <!--- 
     Spring security Filter
     这个Filter会拦截所有的URL请求,并且对这些URL请求进行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>
 < servlet>
  < servlet-name>index< /servlet-name>
  < servlet-class>com.zsw.web.servlet.Index< /servlet-class>
 < /servlet
 < servlet-mapping>
  < servlet-name>index< /servlet-name>
  < url-pattern>/index< /url-pattern>
 < /servlet-mapping>
    < listener>
        < listener-class>org.springframework.web.context.ContextLoaderListener< /listener-class>
    < /listener>    
    < listener>
        < listener-class>com.zsw.web.loader.ServletContextLoaderListener< /listener-class>
    < /listener>
    
< /web-app>

8.2applicationContext-security.xml

< ?xml version= "1.0"  encoding= "UTF-8" ?>
< beans:beans xmlns= "http://www.springframework.org/schema/security"
    xmlns:beans=
"http://www.springframework.org/schema/beans"
    xmlns:xsi=
"http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation=
"http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                        http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd ">
 
 < beans:bean id= "loggerListener"  class= "org.springframework.security.event.authentication.LoggerListener"  />
 < http access-denied-page= "/403.jsp"  >
  < intercept-url pattern= "/static/**"  filters= "none"  />
  < intercept-url pattern= "/template/**"  filters= "none"  />
  < intercept-url pattern= "/"  filters= "none"  />
  < intercept-url pattern= "/login.jsp"  filters= "none"  />
   <!--- 登录认证 -->
     < form-login login-page= "/login.jsp"  authentication-failure-url= "/login.jsp?error=true"  default-target-url= "/index"  />
      <!--- 注销处理 -->
     < logout logout-success-url= "/login.jsp" />
      <!--- 基本认证 -->
     < http-basic />
 < /http>
 < authentication-manager alias= "authenticationManager" />
 
  <!--- 配置UserDetailsService来指定用户和权限  -->
 < authentication-provider user-service-ref= "securityManager" >
  < password-encoder hash= "md5" />
 < /authentication-provider>
 
 < beans:bean id= "accessDecisionManager"  class= "org.springframework.security.vote.AffirmativeBased" >
     < beans:property name= "allowIfAllAbstainDecisions"  value= "false" />
     < beans:property name= "decisionVoters" >
         < beans:list>
             < beans:bean class= "org.springframework.security.vote.RoleVoter" />
             < beans:bean class= "org.springframework.security.vote.AuthenticatedVoter" />
         < /beans:list>
     < /beans:property>
 < /beans:bean>
 
  <!--- 
         由于我们所实现的是FilterSecurityInterceptor中的一个开放接口,
         所以我们实际上定义了一个新的bean,
         并通过<custom-filter after="LAST" />插入到过滤器链中去。
  -->

 < 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"  />
     < beans:property name= "observeOncePerRequest"  value= "false"  />
     < custom-filter after= "LAST"  />
 < /beans:bean>
 
 < beans:bean id= "secureResourceFilterInvocationDefinitionSource"  class= "com.zsw.security.interceptor.SecureResourceFilterInvocationDefinitionSource"  />
 
< /beans:beans>

9. 以上Demo下载地址:

 http://download.csdn.net/source/3500432

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
Spring Security 是一个强大的开源框架,用于提供全面的身份验证(Authentication)和授权(Authorization)解决方案,常用于Java Web应用程序中保护资源访问。它简化了处理用户登录、权限控制以及会话管理等安全相关的任务。 简单授权Demo通常涉及以下几个步骤: 1. 添加依赖:首先在你的Spring Boot项目中添加Spring Security的依赖。如果你使用Maven,可以在pom.xml中添加: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> ``` 2. 配置WebSecurityConfigurerAdapter:创建一个`WebSecurityConfigurerAdapter`子类,并覆盖`configure(HttpSecurity http)`方法,设置基本的安全配置,如登录页面、默认登录处理器等: ```java @Configuration @EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { @Autowired private UserDetailsService userDetailsService; @Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers("/login", "/register").permitAll() .anyRequest().authenticated() .and() .formLogin() .loginPage("/login") .defaultSuccessUrl("/") .failureUrl("/login?error") .and() .logout() .logoutSuccessUrl("/") .deleteCookies("JSESSIONID"); } @Bean public UserDetailsService userDetailsServiceBean() { // 注册用户服务,这里是示例,实际应用中可能需要从数据库获取 return new InMemoryUserDetailsManager(Arrays.asList( new User("user", "password", AuthorityUtils.createAuthorityList("USER")) )); } } ``` 在这个例子中,我们允许"/login"和"/register"路径无需身份验证,所有其他请求都需要认证。登录失败后会被重定向到"/login?error"。 3. 用户认证:定义一个`UserDetailsService`实现,它负责从数据库或内存中加载用户信息进行验证。这里使用的是内存中的简单例子。 4. 创建Controller和视图:定义一个控制器,处理登录请求并渲染登录页面。例如: ```java @Controller public class LoginController { @GetMapping("/login") public String loginPage() { return "login"; } @PostMapping("/login") public String handleLogin(@RequestParam String username, @RequestParam String password, Model model) { // 这里只是一个简单的示例,实际应用中应检查用户名密码是否正确 if ("user".equals(username) && "password".equals(password)) { return "redirect:/"; } else { model.addAttribute("error", "Invalid credentials"); return "login"; } } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值